diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2ed1291..a256755 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,38 +1,21 @@ -name: Deploy Documentation +name: Build v1 Documentation on: push: - branches: - - main - paths: - - 'docs/**' - - 'src/**' - - 'mkdocs.yml' + branches: [main] + paths: ['docs/v1/**', 'mkdocs.yml'] + pull_request: + paths: ['docs/v1/**', 'mkdocs.yml'] workflow_dispatch: permissions: - contents: write + contents: read jobs: - deploy: + build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install uv - uses: astral-sh/setup-uv@v4 - - - name: Install dependencies - run: | - uv sync - uv pip install mkdocs-material "mkdocstrings[python]" mkdocs-gen-files mkdocs-literate-nav mkdocs-section-index - - - name: Build and deploy - run: uv run mkdocs gh-deploy --force + - uses: astral-sh/setup-uv@v4 + - run: uv sync --locked + - run: uv run --no-sync mkdocs build --strict diff --git a/.github/workflows/gpu-tests.yml b/.github/workflows/gpu-tests.yml index 0b74ccb..1c33b06 100644 --- a/.github/workflows/gpu-tests.yml +++ b/.github/workflows/gpu-tests.yml @@ -1,62 +1,16 @@ -name: GPU Verification +name: v1 GPU Verification Pending on: workflow_dispatch: - schedule: - # Nightly at 09:17 UTC — CUDA paths have no other CI coverage - - cron: "17 9 * * *" permissions: contents: read -concurrency: - group: openboost-modal-gpu-verification - cancel-in-progress: false - jobs: - modal-credentials: - name: Check Modal credentials + unavailable: runs-on: ubuntu-latest - outputs: - available: ${{ steps.check.outputs.available }} - steps: - - name: Check whether Modal is configured - id: check - env: - MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }} - MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }} - shell: bash + - name: Explain current v1 boundary run: | - if [[ -n "$MODAL_TOKEN_ID" && -n "$MODAL_TOKEN_SECRET" ]]; then - echo "available=true" >> "$GITHUB_OUTPUT" - else - echo "available=false" >> "$GITHUB_OUTPUT" - echo "::notice::Skipping Modal GPU verification because repository secrets are not configured." - fi - - modal-gpu-tests: - needs: modal-credentials - if: needs.modal-credentials.outputs.available == 'true' - runs-on: ubuntu-latest - timeout-minutes: 45 - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Set up uv - uses: astral-sh/setup-uv@v4 - - - name: Install locked dependencies - run: uv sync --locked --extra bench - - - name: Run CUDA verification on Modal - env: - MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }} - MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }} - run: uv run modal run tests/modal_gpu_tests.py::ci + echo "No v1 CUDA implementation exists. Restore real-device verification at F3; reference tests are not GPU validation." + exit 1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1cfc2e3..ae7cd4b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,46 +1,16 @@ -name: Publish to PyPI +name: v1 Publishing Unavailable on: - release: - types: [published] + workflow_dispatch: -jobs: - test: - runs-on: ubuntu-latest - env: - OPENBOOST_BACKEND: cpu - steps: - - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 - - run: uv sync --extra test --extra sklearn - - run: uv run pytest tests/ --tb=short +permissions: + contents: read - # Bare-install smoke test: no sklearn/test extras, so transitively-pulled - # dependencies (e.g. scipy via scikit-learn) cannot mask a missing runtime dep. - bare-install: - runs-on: ubuntu-latest - env: - OPENBOOST_BACKEND: cpu - steps: - - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 - - run: uv venv && uv pip install . - - run: | - uv run --no-project python -c " - import numpy as np - import openboost as ob - X = np.random.rand(200, 5); y = X[:, 0] + np.random.rand(200) - m = ob.NaturalBoostNormal(n_trees=5); m.fit(X, y); m.predict_interval(X) - print('bare install OK') - " - - publish: - needs: [test, bare-install] +jobs: + unavailable: runs-on: ubuntu-latest - permissions: - id-token: write steps: - - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v4 - - run: uv build - - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Explain current v1 boundary + run: | + echo "The rebuilt package is not release-ready. Implement and verify v1 gates before restoring release publication." + exit 1 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 8b1a1e1..a11d3fc 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,4 +1,4 @@ -name: Tests +name: v1 Reference Checks on: push: @@ -7,97 +7,20 @@ on: branches: [main] jobs: - # Fast tests: <3 min, runs on every PR - fast-tests: + references: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest] python-version: ["3.10", "3.12"] - steps: - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - - name: Install OpenMP runtime (macOS) - if: runner.os == 'macOS' - run: | - brew install libomp - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[test,sklearn]" - pip install "xgboost>=2.0" - - - name: Lint with ruff - run: | - pip install "ruff>=0.4" - ruff check src/openboost/ - - - name: Run fast tests (CPU backend) - env: - OPENBOOST_BACKEND: "cpu" - run: | - pytest tests/ -v --tb=short -m "not slow and not benchmark" - - # Full tests: includes slow tests, runs after fast tests pass - full-tests: - runs-on: ubuntu-latest - needs: fast-tests - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[test,sklearn]" - pip install "xgboost>=2.0" - - - name: Run all tests (CPU backend) - env: - OPENBOOST_BACKEND: "cpu" - run: | - pytest tests/ -v --tb=short - - - name: Run documentation examples (CPU backend) - env: - OPENBOOST_BACKEND: "cpu" - run: | - python scripts/run_doc_examples.py - - # Performance regression check: main branch only - performance-check: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - needs: full-tests - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[test,sklearn]" - pip install "xgboost>=2.0" - - - name: Performance regression check - env: - OPENBOOST_BACKEND: "cpu" - run: | - python benchmarks/check_performance.py + - uses: astral-sh/setup-uv@v4 + - run: uv sync --locked --extra test + - run: uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py + - name: Run current v1 references, not retired production tests + run: uv run --no-sync pytest tests/ -n 0 -q + - run: uv build diff --git a/.gitignore b/.gitignore index 8294445..7b1b665 100644 --- a/.gitignore +++ b/.gitignore @@ -221,4 +221,5 @@ logs/ # Benchmark results (generated) benchmarks/results/*.json +benchmarks/results/scoringbench*/ tasks/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..908c2b6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,237 @@ +# OpenBoost Agent Guide + +This is the canonical repository guidance for coding agents and automated +contributors. Tool-specific instruction files should point here instead of +duplicating policy. + +## Mission + +OpenBoost is a **programmable boosting foundation for researchers and agents**. +The product hypothesis is that readable, composable algorithm components and +verified CPU/CUDA execution reduce the cost of making a correct algorithm change. +Standard GBDT, NaturalBoost/NGBoost-style methods, FormulaBoost, and train-many +are use cases that determine and test the foundation's abstraction boundaries. +Real applications shape the design alongside algorithm families: classification, +regression, ranking, quantiles, multi-output, counts/positive targets, survival, +distributional and structured models, and model selection. All listed use-case +families are required v1 scope, with individual implementation and evaluation +evidence. Do not privilege insurance/AFT or substitute a few representative +successes for complete coverage. Concrete datasets may be selected; required +use cases may not be dropped. See the A1–A13 +[application contracts](planning/foundation-application-contracts.md). + +The user designates this planning round as the real **OpenBoost v1**. Its active +design and execution order is +[`planning/agent-boosting-foundation-plan.md`](planning/agent-boosting-foundation-plan.md). +The concrete construction design is +[`planning/foundation-construction-design.md`](planning/foundation-construction-design.md): +module dependencies, data/state records, composable operations, CPU/CUDA execution, +and B01–B14 build slices. Task specifications and independent oracles do not +constitute the foundation implementation; public components and recipes start in F1. +Execution plans, results and periodic reflections live in +[`v1-sprints/`](v1-sprints/README.md). Read the current sprint before implementing. +Reflect at sprint closure, every three implementation commits, phase transitions, +or architectural/correctness counterexamples; record evidence and deviations there. +Keep cross-sprint learnings in `learnings/` with links to the detailed sprint record. +Required scope is R1–R9/C1–C7/A1–A13; acceptance and quantitative evaluation are in +[`planning/openboost-v1-evaluation.md`](planning/openboost-v1-evaluation.md). +Use the [current release/plan review](planning/boosting-release-review-2026-09-05.md) +for XGBoost, CatBoost and LightGBM baselines; distinguish shipped features from +experimental capabilities, maintainer plans and feature requests. +The user explicitly permits a clean redesign: existing APIs, trainers, internal +representations, and persistence formats need not remain backward compatible. +Preserve mathematical correctness cases and reproducible evidence, not obsolete +interfaces. This is design permission, not a claim that the new architecture exists. + +Do not position the repository as a drop-in replacement for XGBoost, LightGBM, +or CatBoost. A standard recipe does not establish full feature, quality, or speed +parity. Existing experimental capabilities remain experimental until committed, +reproducible artifacts verify their declared scope. + +## Start Here + +Before a non-trivial change: + +1. Read this file and the relevant entries in `learnings/`. +2. Check `git status --short --branch`; preserve unrelated user changes. +3. Read the implementation, its tests, and the public documentation together. +4. Write a short plan for work spanning three or more meaningful steps. +5. Identify the smallest test that can fail before editing. + +Do not trust phase comments, docstrings, README claims, or green CI as proof by +themselves. Verify the actual call path and the tests that exercise it. + +## Current Priority Order + +Current execution map: [Sprint 038 goal/progress review and plan](v1-sprints/038-goal-progress-and-plan.md). +Installed public D2/D3 development wheels pass in Sprint 040 without core edits. +Sprint 041 adds installed ordered Normal/Formula updates through public transactions. +Sprint 042 resolves external result interoperability through the structural +RecipeResult contract and installed mixed M=1/8/32 checks. Sprint 043 adds an installed D1 expectile objective. Next: remaining +D5 author probes, plus remaining OpenBoost real-data adapters. Sprint 044 connects +current A1/A11 workers to all five frozen housing folds; this is validation plumbing, +not real-data quality acceptance. Sprint 045 adds A6 frozen target-scale binding +and original-unit prediction on all five Parkinsons folds. Next: A13 selection +and remaining adapters/D5 checks. Sprint 046 adds training-scale-verified A6 +selection and a synthetic 16-trial current search/release check. Real searches +and remaining adapters/D5 checks are open. Sprint 047 adds verified-scale A6 +standardized quality reporting alongside every per-target gate. Sprint 048 adds +A2/A3 probability adapters and five-fold Adult integration; full Covertype runs +and remaining adapters/searches/D5 are open. Sprint 049 records all five full +Covertype folds timing out at the 90-second fit cap. Next profile the current +CPU path on that same input before expanding adapters; A3 validation is incomplete. +These internal trials +do not establish E5/E7. +Independent validation stopping is implemented in Sprint 039. A6 CPU workflows and shared +preparation/M=1/8/32 fixed-budget equivalence are implemented (Sprints 036–037). +Do not infer F1/B11 readiness from implemented objective count. The +[Sprint 035 audit](v1-sprints/035-cpu-coverage-audit.md) remains historical evidence. + +1. Explicit algorithm tasks, fair baselines, and independent correctness oracles. +2. A minimal CPU foundation tested by structurally different use cases. +3. Evidence that agents can make verified algorithm changes with less work. +4. Verified single-GPU execution and scoped end-to-end cost, including train-many. +5. Real use-case value and independent authors' repeated use. +6. Stabilize packaging and the public contracts justified by that evidence. + +Silent correctness and persistence failures on any exercised path take priority +within every stage. ScoringBench is a distributional quality instrument, not the +gatekeeper for all foundation work. Follow F0–F5 in the active plan; the previous +P0–P7 checklist is a historical implementation/evidence record. + +Treat Ray, multi-GPU, out-of-core training, GOSS speedups, and fused train-many +as experimental. Train-many state semantics are an early design probe; fused +execution and scaling claims require exact correctness and scaling artifacts. +Ray, multi-GPU, and out-of-core expansion remain outside the active plan. +The repository audit in +`learnings/2026-08-15-repository-audit.md` records the current evidence gaps. + +## Architecture + +The user requested retirement of all old production code during Sprint 002. +`src/openboost/` now implements public CPU data/problem records, immutable run +transactions, named statistics and composable split/routing/leaf operations. +Depthwise, best-first and symmetric growers share these operations across numeric, +missing and categorical features, scalar/vector leaves and routed residual solvers. + +Twelve CPU recipes cover squared, binary, multiclass, ranking, quantile, Poisson, +Gamma, fixed-power Tweedie, fixed-scale event/right-censored log-normal AFT, +Normal, saturation Formula and multi-output squared. Normal ordinary/Fisher and +Formula full-GGN updates currently commit jointly. Frequency-severity composition, +class metadata, AFT scale and multi-output inverse scaling have persisted inference +artifacts. PreparedData explicitly reuses fitted training binning/codes across +independent heterogeneous runs. All recipes support independent validation patience +through public StopState, separate from model acceptance and best-model selection. +Execution is sequential; CUDA execution is not implemented. +All A1–A13 real evaluations and formal author/quality/cost gates remain open. + +The user approved B03–B06 construction overlapping unfinished F0.3; see Sprint 018 +and the active plan amendment. Later CPU slices do not establish a formal phase +exit. The last full old implementation is Git revision `50acfc6`; historical +tests/examples require that revision. There is no compatibility shim or legacy +backend in the current package. + +Build the new public data/targets, stats/ops, tree, objectives, runtime, recipes +and artifacts according to the construction design. `tests/v1/reference/` is an +independent mathematical oracle, not the new production backend. The old fixed-bin +sentinel, per-channel trainer and global backend are not new architecture constraints. + +## Correctness Rules + +- A serialization change requires prediction round trips for numeric, + categorical, missing-value, and specialized leaf/tree state that it touches. +- A CUDA change requires CPU/CUDA parity for gradients, splits, leaves, + predictions, and final task metrics—not just matching array shapes. +- Never silently ignore `sample_weight`, exposure, callbacks, evaluation sets, + constraints, or sampling parameters. Support them or reject them explicitly. +- Randomized behavior must be driven by the model's declared seed; do not use + unscoped global `numpy.random` state. +- Distributed child histograms must be derived from routed samples. Scaling a + parent histogram is not an exact substitute. +- Public examples are tests of product behavior. If an example cannot run, fix + it or label the feature experimental before documenting it. + +## Evidence and Benchmark Rules + +- Every performance or quality claim must link to a committed raw artifact. +- Record git SHA and dirty state, dataset/version/hash, split seed, package + versions, OS, CPU/RAM/thread count, GPU/driver/CUDA, and exact CLI arguments. +- Compare end-to-end fit and prediction, including distribution gradients, + transfers, compilation policy, and fallbacks. Kernel microbenchmarks cannot + support an end-to-end product claim. +- Use repeated folds/seeds and publish failures. Compare at matched predictive + quality; do not declare a speed win when CRPS/NLL/calibration regresses. +- Keep official ScoringBench results separate from OpenBoost's large-sample + extension. See `benchmarks/scoringbench/README.md`. +- Synthetic experiments generate hypotheses. Real third-party datasets and + upstream-accepted results generate evidence. + +## Commands + +Use `uv`; do not mutate the project environment with ad-hoc `pip` or Conda +commands. +Current default discovery runs only `tests/v1/`; retained historical tests are +excluded, not counted as passing/skipped v1 coverage. See `tests/README.md`. + +```bash +# Install +uv sync --extra dev +uv sync --extra cuda + +# Focused test while iterating +OPENBOOST_BACKEND=cpu uv run pytest tests/test_file.py -n 0 -q + +# CPU regression suite +OPENBOOST_BACKEND=cpu uv run pytest tests/ -m "not gpu and not benchmark" --tb=short + +# Lint production code and changed support files +uv run ruff check src/openboost/ path/to/changed_file.py + +# Documentation and packaging +uv run mkdocs build +uv build +``` + +Run CUDA tests only on real CUDA hardware. A skipped GPU job is not a passing +GPU validation. ScoringBench has a separate Linux environment documented under +`benchmarks/scoringbench/`. + +## Working and Commit Discipline + +- Use English for all repository prose, including documentation, comments, + instructions, and new sprint/learning records. Preserve literal dataset values, + identifiers, formulas, and raw evidence when translating existing prose. + +- Keep changes small and cohesive. Prefer root-cause fixes over compatibility + shims that conceal invalid state. +- Commit after each independently verified slice: test/benchmark harness, + correctness fix, documentation/learning update, or infrastructure change. +- Do not bundle unrelated cleanup into a fix. Do not amend or rewrite existing + commits unless the user explicitly asks. +- Do not push, publish, create a release, or update an external leaderboard + unless the user asks for that external action. +- Before every commit: inspect the staged diff, run the narrowest meaningful + tests, and include the verification in the relevant learning entry. + +## Learning Log + +`learnings/` is the durable project memory for decisions, failed attempts, +experiments, and non-obvious operational facts. + +- Add or update an entry for every non-trivial change. +- Use `learnings/TEMPLATE.md`. +- Record evidence and falsified hypotheses, not a diary of shell commands. +- Link files and commits. State what was not verified. +- Never include credentials, tokens, private URLs, or user-specific secrets. +- Release notes describe user-facing changes; learning entries explain why the + implementation and evidence changed. + +## Definition of Done + +A change is done only when: + +1. The intended behavior is covered by a focused test or reproducible artifact. +2. Relevant regression tests and lint pass. +3. Documentation and capability claims match the implemented boundary. +4. A learning entry captures important decisions, failures, and follow-ups. +5. The change is committed as a cohesive unit and the remaining work is stated. diff --git a/CLAUDE.md b/CLAUDE.md index 83d6ef9..b7405a8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,169 +1,14 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -OpenBoost is GPU gradient boosting for distributional regression (~20K -lines of Python). Each parameter of the statistical model is a -histogram-tree ensemble, trained with the full K x K natural gradient / -GGN rather than a diagonal approximation. Numba JIT on CPU, -CuPy/numba-cuda on GPU. - -Flagship models: `NaturalBoost` (GAMLSS-style distributions), -`FormulaBoost` (varying-coefficient formula), `WeibullAFT` (censored -Weibull). Mean-regression GBDT, GAM, DART, and linear-leaf models share -the same tree engine. - -Not a faster XGBoost clone. XGBoost/LightGBM remain the right tool for -ordinary MSE/logloss GBDT. - -## Commands - -```bash -# Environment (always use uv, never pip/conda/poetry) -uv sync # Install/sync dependencies -uv sync --extra cuda # With GPU support -uv sync --extra dev # With dev tools (test + bench + sklearn + ruff) - -# Testing (parallelized with pytest-xdist, -n auto is in addopts) -uv run pytest tests/ -v --tb=short # All tests (CPU, parallel) -uv run pytest tests/test_core.py -v # Single test file -uv run pytest tests/test_core.py::test_name -v # Single test -uv run pytest tests/ -n 0 # Force serial (debugging) -OPENBOOST_BACKEND=cuda uv run pytest tests/ # GPU tests -OPENBOOST_BACKEND=cpu uv run pytest tests/ # Force CPU - -# Profiling -uv run python benchmarks/profile_loop.py # Profile training (50K samples default) -uv run python benchmarks/profile_loop.py --summarize # Machine-readable bottleneck summary -OPENBOOST_PROFILE=1 uv run python script.py # Profile any training run via env var - -# Linting -uv run ruff check src/openboost/ # Lint -uv run ruff check src/openboost/ --fix # Autofix - -# Docs -uv run mkdocs serve # Local docs server -uv run mkdocs build # Build docs - -# Build -uv build # Build wheel/sdist -``` - -## Architecture - -### Layer Overview - -``` -Models (_models/) → Trainer (_trainer.py) → Core (_core/) → Backends - ↓ Objective protocol ↓ ↓ -NaturalBoost fit_boosting() fit_tree() _cpu.py -FormulaBoost _objectives.py histograms _cuda.py -WeibullAFT Distribution / Formula / split finding -GradientBoosting AFT / Loss growth -OpenBoostGAM, DART, LinearLeaf -``` - -### Data Layer (`_array.py`) -`BinnedArray` is the fundamental data structure: it quantile-bins continuous features into uint8 (max 255 bins). Missing values encode as `MISSING_BIN = 255`. Native categorical feature support with auto-detection of string/object columns. All tree-building operates on binned data. - -### Core (`_core/`) -- **`_tree.py`**: the main tree-fitting entry points `fit_tree()`, `fit_tree_gpu_native()`, `fit_tree_symmetric()` -- **`_primitives.py`**: Low-level histogram building, split finding, sample partitioning -- **`_growth.py`**: Three growth strategies: `LevelWiseGrowth` (XGBoost-style), `LeafWiseGrowth` (LightGBM-style), `SymmetricGrowth` (CatBoost-style) - -### Backend Dispatch (`_backends/`) -`get_backend()` / `set_backend()` switch between CPU and CUDA implementations. Same interface, different kernels. Control via `OPENBOOST_BACKEND` env var or `set_backend('cuda')`. Use `backend_context('cpu')` context manager for temporary switches. - -### Models (`_models/`) -- **`_boosting.py`**: `GradientBoosting`, `MultiClassGradientBoosting` -- **`_distributional.py`**: `NaturalBoost` / `DistributionalGBDT` (facade over the unified trainer) -- **`_formula.py`**: `FormulaBoost` with user `f(θ, x)`, FD Jacobian, GGN preconditioner -- **`_survival.py`**: `WeibullAFT` with censored NLL, expected-Fisher natural gradient -- **`_sklearn.py`**: sklearn wrappers (`OpenBoostRegressor`, `OpenBoostClassifier`, `OpenBoostDARTRegressor`, `OpenBoostGAMRegressor`, `OpenBoostDistributionalRegressor`, `OpenBoostLinearLeafRegressor`) -- **`_dart.py`**, **`_linear_leaf.py`**, **`_gam.py`**: DART, linear-leaf, GAM - -### Trainer (`_trainer.py`) + objectives (`_objectives.py`) -Single `fit_boosting(objective, X, y, ...)` loop. Facades supply an -`Objective` (`DistributionObjective`, `FormulaObjective`, -`WeibullAFTObjective`, `LossObjective`). GPU path: device-resident raw -scores when the objective is device-capable; `fit_tree_gpu_native` per -channel. - -### Persistence (`_persistence.py`) -`PersistenceMixin` provides `save()`/`load()` on all models. Generic `ob.load(path)` auto-detects model class from saved state. - -### Profiling (`_profiler.py`) -`ProfilingCallback` instruments training by wrapping core primitives (`build_node_histograms`, `find_node_splits`, `partition_samples`, `compute_leaf_values`, `fit_tree`) with timers. Outputs JSON reports to `logs/` with per-phase breakdown, bottleneck identification, and run-over-run comparison. CLI runner: `benchmarks/profile_loop.py`. - -### Loss Functions (`_loss.py`) -9 objectives, each with CPU/GPU/dispatcher implementations returning `(gradient, hessian)`. Custom losses are callables with signature `fn(pred, y) -> (grad, hess)`; register by name via `register_loss` (optional `loss_value_fn` for true loss reporting), and mark GPU-capable losses with `@ob.device_loss`. `register_distribution` / `register_growth_strategy` extend the other factories. - -### Distributions (`_distributions.py`) -8 distributional families for NaturalBoost (Normal, LogNormal, Gamma, Poisson, StudentT, Tweedie, NegativeBinomial). Each implements `nll_grad_hess()` for natural gradient computation. - -## Key Conventions - -- **Python 3.10+** target. Ruff rules: E, F, I, UP, B, SIM (line length 100; E501, E402, F821 ignored). -- **uv only** for package management: never `pip install` or `conda`. -- All Numba-jitted functions use `@njit` or `@cuda.jit`. CPU kernels are in `_backends/_cpu.py`, CUDA in `_backends/_cuda.py`. -- Test environment variable `OPENBOOST_BACKEND=cpu` forces CPU backend in CI. -- Tests use `pytest-xdist` (`-n auto --dist loadfile`) for parallel execution. Shared fixtures are in `tests/conftest.py` (session-scoped datasets, function-scoped gradients). -- **GPU-native builder** (`fit_tree_gpu_native`) does not support missing values or categorical features. The training loop in `_boosting.py` auto-falls back to `fit_tree()` with a warning when the data has NaN or categorical columns. -- **Callbacks**: every model (`GradientBoosting`, `MultiClassGradientBoosting`, `DART`, `NaturalBoost`/`DistributionalGBDT`, `FormulaBoost`, `WeibullAFT`, `LinearLeafGBDT`, `OpenBoostGAM`) supports `callbacks` and `eval_set` in `fit()`. FormulaBoost eval tuples are `(X, y, model_input)`; WeibullAFT tuples are `(X, y[, event])`. -- **`random_state`**: `GradientBoosting` and `DART` accept `random_state` for reproducibility. Sklearn wrappers pass it through. DART also accepts `seed` (alias). -- **`suggest_params()`**: Returns sklearn-style names by default (`n_estimators`). Pass `style='core'` to get core API names (`n_trees`). -- **Profiling**: `ProfilingCallback` wraps core primitives with timers. Enable via callback or `OPENBOOST_PROFILE=1` env var. Reports go to `logs/` as JSON. - -## Working Style - -### 1. Plan Mode Default -- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions) -- If something goes sideways, STOP and re-plan immediately -- don't keep pushing -- Use plan mode for verification steps, not just building -- Write detailed specs upfront to reduce ambiguity - -### 2. Subagent Strategy -- Use subagents liberally to keep main context window clean -- Offload research, exploration, and parallel analysis to subagents -- For complex problems, throw more compute at it via subagents -- One task per subagent for focused execution - -### 3. Self-Improvement Loop -- After ANY correction from the user: update `tasks/lessons.md` with the pattern -- Write rules for yourself that prevent the same mistake -- Ruthlessly iterate on these lessons until mistake rate drops -- Review lessons at session start for relevant project - -### 4. Verification Before Done -- Never mark a task complete without proving it works -- Diff behavior between main and your changes when relevant -- Ask yourself: "Would a staff engineer approve this?" -- Run tests, check logs, demonstrate correctness - -### 5. Demand Elegance (Balanced) -- For non-trivial changes: pause and ask "is there a more elegant way?" -- If a fix feels hacky: "Knowing everything I know now, implement the elegant solution" -- Skip this for simple, obvious fixes -- don't over-engineer -- Challenge your own work before presenting it - -### 6. Autonomous Bug Fixing -- When given a bug report: just fix it. Don't ask for hand-holding -- Point at logs, errors, failing tests -- then resolve them -- Zero context switching required from the user -- Go fix failing CI tests without being told how - -## Task Management - -1. **Plan First**: Write plan to `tasks/todo.md` with checkable items -2. **Verify Plan**: Check in before starting implementation -3. **Track Progress**: Mark items complete as you go -4. **Explain Changes**: High-level summary at each step -5. **Document Results**: Add review section to `tasks/todo.md` -6. **Capture Lessons**: Update `tasks/lessons.md` after corrections - -## Core Principles - -- **Simplicity First**: Make every change as simple as possible. Impact minimal code. -- **No Laziness**: Find root causes. No temporary fixes. Senior developer standards. +The canonical repository guidance is [`AGENTS.md`](./AGENTS.md). Read it in full +before changing code, benchmarks, documentation, CI, packaging, or release +state. + +Claude-specific compatibility notes: + +- Use `uv` for environments and commands. +- Store durable decisions, failed experiments, and user corrections in + `learnings/`, not the ignored `tasks/` directory. +- Keep commits small and verified. Do not push or publish unless requested. +- Treat GPU, distributed, out-of-core, and performance claims according to the + evidence gates in `AGENTS.md`; comments and green-but-skipped CI are not proof. diff --git a/README.md b/README.md index 01f7ddb..d6e6d12 100644 --- a/README.md +++ b/README.md @@ -1,128 +1,104 @@ # OpenBoost -**GPU gradient boosting for distributional regression.** - -Every parameter of `F(y | x)` gets its own tree ensemble, updated with the full -`K×K` natural gradient rather than a diagonal approximation. `FormulaBoost` -extends the same engine to varying-coefficient formulas `y = f(θ(z), x)`. - -> 1.0.0rc1. APIs may still move, so install with `--pre` until 1.0. - -## Install +**A programmable boosting foundation for researchers and AI agents.** + +OpenBoost v1 is being rebuilt around composable algorithm components, ordinary +Python recipes and explicit CPU/CUDA execution. The goal is to reduce the cost +of making a correct, reproducible algorithm change. + +## Current state + +This checkout is **under construction**. The retired implementation is not restored. +Initial public CPU components now provide owned numeric inputs, explicit problems, +run identity, immutable proposal/accept/reject state and mapped tree/constant ensemble +artifacts. See [B03 usage and boundaries](docs/v1/cpu-state.md). + +[Categorical input and equality splits](docs/v1/categorical.md) support typed +train-only dictionaries and explicit missing/unseen routing. + +Public [numeric operations](docs/v1/numeric-ops.md) now add quantile binning, +weighted row fields, histograms, split callbacks, routing and scalar/vector leaves. +Public [depthwise, best-first and symmetric growers](docs/v1/trees.md) compose +these operations and persist validated numeric/categorical trees. The first complete [squared-error recipe](docs/v1/squared.md) +and [Normal recipe](docs/v1/normal.md) support weights, offsets and +fixed/backtracking steps on CPU. Normal exposes ordinary/Fisher directions and +joint mean/log-scale updates. [Formula and sequential runs](docs/v1/formula-runs.md) +add structured full-metric updates and independent heterogeneous jobs. CUDA +execution is not implemented yet. [Binary classification](docs/v1/binary.md) now +persists typed class order and exposes probability/label inference. +[Multiclass and vector leaves](docs/v1/multiclass.md) add joint softmax updates +and separate split/leaf statistics with arbitrary output mappings. + +Independent references and comparator/data checks remain evaluation preparation. +F0.3 is still open; the user approved overlapping B03–B06 construction without +removing any v1 scope or acceptance requirements. No real quality, GPU performance +or agent/adoption advantage has been established for the new foundation. + +- [Execution and reflections](v1-sprints/README.md) +- [Construction design](planning/foundation-construction-design.md) +- [v1 plan](planning/agent-boosting-foundation-plan.md) +- [Required tasks](planning/foundation-tasks.md) +- [Acceptance and evaluation](planning/openboost-v1-evaluation.md) + +All R1–R9 / C1–C7 / A1–A13 remain required. Classification, regression, ranking, +quantiles, multi-output, count/positive/aggregate targets, survival, distributional +and formula models, and train-many each need their own implementation and evidence. + +## Development ```bash -pip install --pre openboost # core -pip install --pre "openboost[cuda]" # GPU trees -pip install --pre "openboost[sklearn]" # sklearn wrappers -``` - -Python 3.10+. NVIDIA GPU optional (CUDA 11/12). - -## Distributional regression - -**NaturalBoost** predicts a distribution instead of a point. Every parameter -of `F(y | x)` gets its own tree ensemble, trained by natural gradient. - -```python -import openboost as ob - -model = ob.NaturalBoostNormal(n_trees=500, max_depth=3, learning_rate=0.03) -model.fit(X_train, y_train) - -mean = model.predict(X_test) -lo, hi = model.predict_interval(X_test, alpha=0.1) # 90% interval +uv sync --extra test +uv run pytest tests/ -n 0 -q +uv run ruff check src/openboost tests/v1 tests/conftest.py +uv build ``` -**WeibullAFT** takes the same idea to right-censored survival. Both the scale -and the shape vary by covariate, so the hazard shape is per row rather than -one global hyperparameter. - -```python -import openboost as ob - -model = ob.WeibullAFT(n_trees=300, max_depth=3) -model.fit(Z_train, time_train, event=observed) # 1 = event, 0 = censored - -params = model.predict_params(Z_test) # {scale, shape} -t_hat = model.predict(Z_test) # median time -s = model.predict_survival(Z_test, t=5.0) # S(5 | z) -``` - -## Varying-coefficient models - -**FormulaBoost** boosts the coefficients of a formula you write. Given -`y = f(θ, x)`, the trees learn `θ(z)` while `x` enters only through `f`. - -```python -import numpy as np -import openboost as ob - -def sales(theta, x): - a, b = theta - return a * x ** (1.0 / (1.0 + np.exp(-b * x))) - -model = ob.FormulaBoost( - formula=sales, n_params=2, links=("log", "identity"), - param_names=("a", "b"), precond="full", -) -model.fit(Z_train, y_train, model_input=x_train) - -params = model.predict_params(Z_test) # per-row a(z), b(z) -yhat = model.predict(Z_test, model_input=x_new) -``` - -## Mean regression - -The single-parameter case of the same engine: `GradientBoosting`, -`OpenBoostGAM`, DART, linear-leaf models, and sklearn wrappers. They are here -because they share the trainer and the tree code, not because they beat -XGBoost or LightGBM at plain MSE or logloss. Those are optimized C++ and -should stay your default for point estimates. +Python 3.10+. Current tests are CPU-only reference checks. CUDA is a future +required execution subset, not an implemented capability of this reset checkout. +GPU and publishing workflows stay unavailable until their v1 gates are met. -## How it compares +## Historical implementation and evidence -Each library optimizes for a different target. NGBoost introduced -natural-gradient distributional boosting and stays close to sklearn on CPU. -XGBoost is the reference for fast mean regression, and its custom-objective -API takes a diagonal Hessian, which is a sound trade for that goal but cannot -represent the off-diagonal coupling between formula parameters; `survival:aft` -likewise holds the Weibull shape fixed across rows. OpenBoost gives up C++ -speed on plain regression in exchange for the full metric and an open model -class. +Revision `50acfc6` is the last revision containing the old production code plus +Sprint 001 references. Use that revision in a separate checkout to reproduce +old APIs, examples and experiments; no compatibility layer remains here. -| | NGBoost | XGBoost | OpenBoost | -| --------------------------- | ----------------------------------------- | -------------------------------------- | ---------------------------------- | -| What varies with covariates | Distribution parameters (fixed catalogue) | The mean, or a diagonal custom objective | Distribution or formula parameters | -| Metric | Natural gradient | Diagonal Hessian | Fisher / full GGN | -| GPU trees | No | Yes | Yes | -| Weibull shape `k(z)` | n/a | Global hyperparameter | Per row | +Historical tests, examples, documentation and benchmark artifacts are retained +as evidence and sources of mathematical counterexamples. Default test discovery +runs `tests/v1/` only. Old tests are not counted as v1 passes or skips. +The current documentation build uses `docs/v1/`; other documentation describes +the retired implementation. Published packages and historical results do not +establish the new architecture's quality, speed or adoption. -## Benchmarks +[Query-local ranking](docs/v1/ranking.md) adds pairwise/lambda CPU geometry and +fixed-step recipes with validation NDCG selection. Real A4 evaluation remains open. -Early and incomplete, so read them as directional rather than settled. -NaturalBoost matches NGBoost's NLL on the UCI datasets measured so far and -trains in seconds on an A100 at sizes where NGBoost, which is CPU-only, takes -most of an hour. FormulaBoost and WeibullAFT recover parameter surfaces that a -diagonal-Hessian objective cannot. Three UCI datasets have not been measured, -and XGBoostLSS and LightGBMLSS are not in the comparison yet. +[Quantile and penalized leaves](docs/v1/quantile.md) expose routed residuals/original +weights and compose all three CPU growth policies. Real A5 evaluation remains open. -Numbers, caveats, and reproduce commands are on the -[benchmarks page](https://jxucoder.github.io/openboost/benchmarks/). +[Poisson counts and exposure](docs/v1/poisson.md) add a CPU count recipe with explicit +rate/count outputs. Real A7 evaluation remains open. -## Documentation +[Gamma positive-target means](docs/v1/gamma.md) add weighted CPU mean regression. +Real A8 quality and distributional calibration remain unverified. -**[jxucoder.github.io/openboost](https://jxucoder.github.io/openboost)** +[Tweedie nonnegative means](docs/v1/tweedie.md) support fixed-power CPU fitting and +explicit annualized-loss weight semantics. Real A9 evaluation remains open. -- [Quickstart](https://jxucoder.github.io/openboost/getting-started/quickstart/) -- [How it works](https://jxucoder.github.io/openboost/user-guide/how-it-works/) -- [NaturalBoost](https://jxucoder.github.io/openboost/user-guide/naturalboost/overview/) -- [FormulaBoost](https://jxucoder.github.io/openboost/user-guide/formulaboost/) -- [Weibull AFT](https://jxucoder.github.io/openboost/user-guide/survival/) -- [Benchmarks](https://jxucoder.github.io/openboost/benchmarks/) -- [API reference](https://jxucoder.github.io/openboost/api/openboost/) +[Frequency–severity composition](docs/v1/frequency-severity.md) binds matched paid-loss aggregates +and persists two-model inference with explicit output units. Real A9 evaluation remains open. +[Log-normal AFT](docs/v1/aft.md) adds event/right-censored CPU training and +persisted scale-aware survival outputs. Real A10 evaluation remains open. +[Current CPU coverage audit](v1-sprints/035-cpu-coverage-audit.md) identifies +external author workflows and real-data integration as remaining CPU +prerequisites; Sprint 036 supplies the audited A6 recipe/scaling gap. -## License +[Multi-output squared regression](docs/v1/multioutput.md) supports independent/shared trees, +projected splits and persisted training-only target scaling. Real A6 evaluation remains open. -Apache 2.0 \ No newline at end of file +[Shared training preparation](docs/v1/preparation.md) reuses fitted CPU binning/codes +across independent jobs, verified at M=1/8/32. +[Independent stopping](docs/v1/stopping.md) adds validation patience to every CPU +recipe while keeping model acceptance and best-model selection independent. diff --git a/benchmarks/check_performance.py b/benchmarks/check_performance.py index bc8aa5c..7fd4e90 100644 --- a/benchmarks/check_performance.py +++ b/benchmarks/check_performance.py @@ -1,10 +1,11 @@ -"""Performance regression check for CI. +"""Performance regression check for CI or a local baseline. Runs a fixed, small benchmark and compares against stored baselines. Fails if any metric degrades by more than 20%. Usage: - uv run python benchmarks/check_performance.py + uv run python benchmarks/check_performance.py --baseline baseline.json + uv run python benchmarks/check_performance.py --benchmark-only --output result.json uv run python benchmarks/check_performance.py --update-baselines """ @@ -12,6 +13,9 @@ import argparse import json +import os +import platform +import subprocess import sys import time import tracemalloc @@ -20,7 +24,6 @@ import numpy as np PROJECT_ROOT = Path(__file__).parent.parent -sys.path.insert(0, str(PROJECT_ROOT / "src")) BASELINE_FILE = Path(__file__).parent / "results" / "performance_baselines.json" @@ -90,17 +93,43 @@ def run_fixed_benchmark(): } -def save_baselines(results): - """Save results as new baselines.""" - BASELINE_FILE.parent.mkdir(parents=True, exist_ok=True) - with open(BASELINE_FILE, "w") as f: +def collect_provenance(source_root: Path) -> dict[str, str]: + """Collect enough environment data to interpret a raw CI result.""" + import openboost as ob + + commit = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=source_root, + check=False, + capture_output=True, + text=True, + ) + git_commit = commit.stdout.strip() if commit.returncode == 0 else "unknown" + + return { + "git_commit": git_commit, + "openboost_version": ob.__version__, + "python_version": platform.python_version(), + "numpy_version": np.__version__, + "platform": platform.platform(), + "processor": platform.processor() or "unknown", + "openboost_backend": os.environ.get("OPENBOOST_BACKEND", "auto"), + "numba_num_threads": os.environ.get("NUMBA_NUM_THREADS", "default"), + "numba_cache_dir": os.environ.get("NUMBA_CACHE_DIR", "default"), + } + + +def save_results(results, path: Path): + """Save benchmark results.""" + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w") as f: json.dump(results, f, indent=2) - print(f"Baselines saved to {BASELINE_FILE}") + print(f"Results saved to {path}") -def load_baselines(): +def load_baselines(path: Path): """Load stored baselines.""" - with open(BASELINE_FILE) as f: + with open(path) as f: return json.load(f) @@ -140,10 +169,52 @@ def main(): "--update-baselines", action="store_true", help="Update baselines with current results" ) + parser.add_argument( + "--baseline", + type=Path, + default=BASELINE_FILE, + help="Baseline JSON to compare against", + ) + parser.add_argument( + "--output", + type=Path, + help="Write the current raw benchmark result to this JSON file", + ) + parser.add_argument( + "--benchmark-only", + action="store_true", + help="Run and save the benchmark without comparing it", + ) + parser.add_argument( + "--source-root", + type=Path, + default=PROJECT_ROOT, + help="Repository root whose src/openboost implementation should run", + ) args = parser.parse_args() + if ( + not args.update_baselines + and not args.benchmark_only + and not args.baseline.exists() + ): + print(f"No baseline found at {args.baseline}", file=sys.stderr) + print( + "Pass --baseline, use --benchmark-only, or explicitly create a " + "local baseline with --update-baselines.", + file=sys.stderr, + ) + sys.exit(2) + + source_dir = args.source_root.resolve() / "src" + if not source_dir.is_dir(): + parser.error(f"source root has no src directory: {args.source_root}") + sys.path.insert(0, str(source_dir)) + print("Running fixed benchmark...") results = run_fixed_benchmark() + results["benchmark_schema_version"] = 1 + results["provenance"] = collect_provenance(args.source_root.resolve()) print(f" fit_time: {results['fit_time_median']:.4f}s") print(f" predict_time: {results['predict_time_median']:.4f}s") @@ -151,17 +222,17 @@ def main(): print(f" mse: {results['mse']:.6f}") print(f" r2: {results['r2']:.4f}") - if args.update_baselines: - save_baselines(results) + if args.output: + save_results(results, args.output) + + if args.benchmark_only: return - if not BASELINE_FILE.exists(): - print(f"\nNo baselines found at {BASELINE_FILE}") - print("Run with --update-baselines to create them.") - save_baselines(results) + if args.update_baselines: + save_results(results, args.baseline) return - baselines = load_baselines() + baselines = load_baselines(args.baseline) regressions = check_regression(results, baselines) if regressions: diff --git a/benchmarks/foundation/README.md b/benchmarks/foundation/README.md new file mode 100644 index 0000000..a63c337 --- /dev/null +++ b/benchmarks/foundation/README.md @@ -0,0 +1,211 @@ +# Foundation GPU evidence + +This entrypoint is isolated from `tests/modal_gpu_tests.py`: the legacy app +registers source-mounted jobs and broad dependencies. Loading it would defeat +the installed-wheel boundary and build unrelated images. Existing jobs remain +available; this app uploads only its allowlisted bundle with automatic source +inclusion disabled. + +From a clean committed checkout: + +```bash +uv run python -m benchmarks.foundation.prepare +uv run modal run benchmarks/foundation/modal_app.py::foundation_smoke +``` + +The bundle is generated in ignored `build/foundation/`. Stale/dirty source, +modified bundle files, failing/missing/skipped/duplicate required tests, +timeouts, incorrect wheel provenance and silent objective fallback fail the +command. Results are written before validation so failed jobs retain evidence. +An image-build failure before the local entrypoint starts is reported by Modal +itself and must be recorded separately; it is not a completed GPU test. + +The smoke uses one T4, two CPU cores, 8 GiB requested memory, one container, +no application retries, a 300-second remote function timeout and a 240-second +pytest subprocess limit. Platform startup/build/restarts are outside this +execution timing. No cost or scaling claim follows from the smoke. + +The two mandatory cases check installed wheel contents, CuPy/Numba pointer +sharing and owner lifetime with a real kernel, and a two-round Normal fit +whose gradients and native tree calls actually run on device. Private imports +and spies here are test instrumentation, not the public extension examples +planned for P6. The small dataset is training-only; NLL is a finite-value +sanity check, not evidence of held-out quality or complete CPU/CUDA parity. + +Offline revalidation: + +```bash +uv run python -m benchmarks.foundation.runner benchmarks/results/foundation/RUN_ID +``` + +Refresh the hash-locked requirements only when changing the environment: + +```bash +uv export --locked --extra cuda --extra test --no-dev --no-emit-project --prune jax --prune jaxlib --no-annotate --no-header -o benchmarks/foundation/requirements.txt +``` + +Markers remain in the export and are evaluated on Linux. JAX is not required +for these NumPy/CuPy/Numba smoke cases. Record the exact installed versions in +each run. The CUDA base digest is Linux amd64 CUDA 12.4.0 devel Ubuntu 22.04, +resolved from NVIDIA's registry; the image's Python patch version is recorded +at runtime. The uv image installer is pinned to 0.12.1. + +P2 weighted correctness regression (includes the smoke cases): + +```bash +uv run python -m benchmarks.foundation.prepare --suite correctness +uv run modal run benchmarks/foundation/modal_app.py::foundation_correctness +``` + +This currently covers fixed-bin weighted histograms/Newton predictions and +three-round weighted Normal/Poisson CPU/CUDA comparisons. It does not yet +constitute the complete P2 baseline gate. + +## Remaining P2 execution boundaries and baseline + +The `boundaries` suite adds eight real-device tests to the five correctness +cases: custom/exposure/generic fallback, device-error rollback, row/column +sampling preflight and Normal/Poisson callback/eval/cross-device persistence. + +```bash +uv run python -m benchmarks.foundation.prepare --suite boundaries +uv run modal run benchmarks/foundation/modal_app.py::foundation_boundaries +``` + +The `baseline` suite includes all 13 boundary/correctness cases and stops on +any failure before entering its real-data matrix. Download the public +California Housing archive once into the ignored data directory: + +```bash +uv run python -c 'from benchmarks.foundation.dataset import fetch; fetch("build/foundation_data/cal_housing.tgz")' +uv run python -m benchmarks.foundation.prepare --suite baseline +uv run modal run benchmarks/foundation/modal_app.py::foundation_baseline +``` + +The archive hash is sklearn 1.8.0's published hash, and the transformation was +checked against that installed sklearn loader. `housing.json` freezes the +archive/array/split hashes. Float32 conversion follows the original per-row +ratio transformations; no learned scaling is applied. Each seed 0/1/2 uses a +60/20/20 train/validation/test permutation. Only training data fits bin edges. + +The predefined Normal model uses 30 rounds, depth 3, learning rate .05 and 64 +bins. For every CPU/CUDA × seed × no-eval/eval cell, a fresh Python subprocess +and empty NUMBA_CACHE_DIR measure first and repeated fit. Fits include binning, +objective math, copies and compilation; imports, dataset loading and container +startup are excluded. Prediction includes test binning. Small path-counting +wrappers are included in timings. Repeated CUDA predictions use rtol=2e-5 / +atol=2e-6 because floating-point atomic reductions need not be bit-identical. + +CPU/CUDA quality gates are frozen before collection: per seed/mode NLL absolute +difference <= .01 * max(1, abs(CPU NLL)), CRPS regression <= 1%, and coverage90 +absolute difference <= .01. These real-data gates cannot override the strict +micro-oracle tests. Three seeds do not establish statistical significance. + +The baseline function is limited to one T4, two CPU cores, 8 GiB requested +memory, no retries and 1800 seconds. Its pytest subprocess is capped at 1740 +seconds and each matrix worker at 150 seconds. Failure output and partial +completed cells are retained. Exact CPU model is recorded if /proc exposes it. +The trainer transfer counter is partial, and is not total PCIe traffic or a +zero-transfer assertion. GPU memory peaks and scaling remain later gates. + +Status: P2.2/P2.3 completed on real T4: 14 passed, no skips, all 12 baseline +cells completed. See [raw evidence and scoped timing/quality results](../results/foundation/20260905T084129Z-2574e387/README.md). +First-fit timings do not clear CUDA driver caches; the container does not +expose its physical CPU model. These limits are recorded in the artifact. + +P4.1 histogram validation uses `prepare --suite histograms` and Modal entrypoint +`foundation_histograms`. It uploads the independent CPU test module as +`histogram_oracle.py`, runs the existing two smoke cases plus one comprehensive +batch histogram device case, and requires all three without skips. The device +case covers small exact, random and empty aggregates; this is not a GPU extension +trainer or performance benchmark. + +P4.2 uses `prepare --suite splits` and entrypoint `foundation_splits`, adding +`test_splits.py` and the CPU row-mask oracle (`split_oracle.py`). It reruns the +two smoke cases and histogram case, then verifies split/gain/routing, exact ties, +invalid routes, and rebuilding child histograms from real routed rows. All four +GPU cases and their evidence fields are required; no skips satisfy the gate. + +P4.3 uses `prepare --suite leaves` / `foundation_leaves`. The allowlist adds +`test_leaves.py` and `leaf_oracle.py`; all five smoke/histogram/split/leaf cases +must pass. Leaf evidence checks direct row sums, empty/zero-curvature behavior, +plugin output/ownership errors and two rounds where clipping changes the next +weighted gradient. The GPU two-round check composes primitives; it is not an +assembled experimental GPU Booster validation. + +P4.4 uses `prepare --suite builder` / `foundation_builder`, adding `test_builder.py` +and the whole-tree row oracle `builder_oracle.py`. Six required cases include +prior primitives, whole-tree topology/leaf checks, input-view lifetime, +compact-only finalization, two-round/two-channel Normal composition at 16 and +4,097 rows, bounded rules, and CPU inference after persistence. Synthetic +NLL/CRPS check numerical agreement; this is not a product-quality benchmark. + + +`--suite trainer` / `foundation_trainer` extends the seven-case suite to actual +strict experimental Booster.fit. It checks default/external dispatch, weighted +scheduled Normal parity and CPU persistence/rollback, all Normal/Poisson × +ordinary/natural adapter modes and invalid-statistic rejection. Counts and +reports distinguish compact downloads, CPU initialization/binning and device +input copies. The frozen run has no nsys executable: profiler evidence is open. +See `results/foundation/20260905T180000Z-7d73ba83` under benchmarks for raw evidence. + + +`--suite extensions` / `foundation_extensions` installs the two independent +example wheels alongside OpenBoost. The three-case suite checks installed module +contents, independent GPU mathematics, eight CPU/CUDA composition cells and a +standalone GPU demo, then removes the plugins and checks nine exact CPU model +roundtrips in a new interpreter. `20260905T181351Z-1aee9568` is the passing raw +artifact; the initial standalone-script failure is retained separately. + +## P7 resident value protocol + +`prepare --suite value` freezes the P2 Housing archive, splits, configuration and +raw baseline plus both independent 0.2.0 wheels. Run +`uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_value`. +One T4 (2 CPU, 8 GiB, 1800-second function limit, no retry) executes seeds 0/1/2 +and legacy CPU, legacy CUDA, strict experimental CUDA, independent A+B+C CUDA. +Each cell uses a fresh subprocess and fresh Numba/CuPy cache directories, one +process-first fit and three warm fits. Imports, data loading and context startup +are excluded; binning, gradients, transfers and fit compilation are included. +The driver cache is not cleared: this is not a machine-cold benchmark. + +Compare the default candidate with the same-run legacy CUDA reference, and check +that reference against frozen P2 held-out quality. Historical first-fit timings +have a different cache policy and are not a direct latency comparator. Require +NLL difference <= 0.01 * max(1, abs(reference)), CRPS <= 1.01 * reference and +coverage90 difference <= 0.01 on every repeat/seed. Report a regression instead +of rejecting its artifact. A ratio of cross-seed median warm fit times above +1.2 triggers design review. The independent Fisher/bounded/scheduled algorithm +uses bound=0.5 and tau=1 without tuning; its math differs and its quality/cost +is reported separately. Strict CUDA eval/callbacks remain unsupported; the P2 +eval cells have no strict-GPU performance counterpart. + +A separate fifth fit records cProfile host attribution and named transfer wrappers; +a sixth memory-only fit samples device-wide used memory every 5 ms. Inclusive times and nested +wrapper counts overlap; they are not kernel times or a complete transfer audit. +Sampled memory is a lower bound including contexts and allocator caches, not an +exact per-fit peak. CuPy pool figures exclude Numba allocations. A CUDA trace is +still a separate evidence gap. Report T4 seconds, not inferred billing dollars. + + +The original P7 value run used concurrent profiling/sampling; its cProfile times +were contaminated by the sampler and remain retained with that limitation. +`prepare --suite value_profile` / `foundation_value_profile` collect only seed 0 +for the four strategies, one compile warmup then isolated host and memory fits. +The 600-second diagnostic job does not replace the original timing matrix. +It checks quality against that committed parent result, carries its hash, and +adds synchronized inclusive boundary timers. Nested timers overlap and include +synchronization overhead; they are diagnostic, not production latency claims. + + +P7 outcome: [original resident matrix](../results/foundation/20260905T183820Z-3c245f2d/README.md) +passes default quality but fails the performance budget (13.899x legacy CUDA fit +median). The [isolated diagnostic](../results/foundation/20260905T184856Z-dcd49569/README.md) +points to tree construction and its boundary as the dominant cost. Neither run +establishes a general GPU speed/cost advantage or external adoption. + + +[Fixed-slot growth follow-up](../results/foundation/20260905T193308Z-5ebd75ab/README.md) +retains all checks and records default warm fit 1.868 s versus the original +2.079 s. The paired legacy ratio is still 12.888x: quality passes, performance +budget fails. CUDA correctness and independent CPU wheel evidence accompany it. diff --git a/benchmarks/foundation/__init__.py b/benchmarks/foundation/__init__.py new file mode 100644 index 0000000..55fc70a --- /dev/null +++ b/benchmarks/foundation/__init__.py @@ -0,0 +1 @@ +"""Reproducible, wheel-installed foundation verification.""" diff --git a/benchmarks/foundation/baseline_worker.py b/benchmarks/foundation/baseline_worker.py new file mode 100644 index 0000000..e502769 --- /dev/null +++ b/benchmarks/foundation/baseline_worker.py @@ -0,0 +1,175 @@ +"""One fresh-process baseline cell: first fit and repeated fit, same inputs.""" + +import hashlib +import json +import sys +import time +import warnings +from pathlib import Path + +import numpy as np +from scipy.special import ndtr, ndtri + +if __package__: + from .dataset import load_housing, split_indices +else: + from dataset import load_housing, split_indices + +CONFIG = dict( + n_trees=30, + max_depth=3, + learning_rate=0.05, + n_bins=64, + min_child_weight=1.0, + reg_lambda=1.0, + subsample=1.0, + colsample_bytree=1.0, +) + + +def normal_metrics(y, params): + mu, sigma = (np.asarray(params[k], dtype=np.float64) for k in ("loc", "scale")) + if not (np.all(np.isfinite(mu)) and np.all(np.isfinite(sigma)) and np.all(sigma > 0)): + raise ValueError("Invalid Normal parameters") + z = (y - mu) / sigma + return { + "nll": float(np.mean(np.log(sigma) + 0.5 * np.log(2 * np.pi) + 0.5 * z**2)), + "crps": float( + np.mean( + sigma + * ( + z * (2 * ndtr(z) - 1) + + 2 * np.exp(-z * z / 2) / np.sqrt(2 * np.pi) + - 1 / np.sqrt(np.pi) + ) + ) + ), + "coverage90": float(np.mean(np.abs(z) <= ndtri(0.95))), + } + + +def run_cell(backend, seed, mode, archive): + import openboost as ob + import openboost._trainer as trainer + from openboost._objectives import DistributionObjective + + X, y = load_housing(archive) + train, val, test = split_indices(len(y), seed) + # No scaling or target fitting outside train; fit bins inside timed model.fit. + X_train, y_train = X[train], y[train] + X_val, y_val, X_test, y_test = X[val], y[val], X[test], y[test] + + def sync(): + pass + + if backend == "cuda": + from numba import cuda + + sync = cuda.synchronize + counts = {} + original_tree, original_step, original_host = ( + trainer.fit_tree_gpu_native, + DistributionObjective.step, + trainer._to_host, + ) + + def native(*args, **kwargs): + counts["native_tree_calls"] += 1 + return original_tree(*args, **kwargs) + + def step(self, raw, *args, **kwargs): + output = original_step(self, raw, *args, **kwargs) + if backend == "cuda": + assert all(hasattr(a, "__cuda_array_interface__") for a in raw.values()) + assert all( + hasattr(a, "__cuda_array_interface__") for pair in output.values() for a in pair + ) + counts["objective_calls"] += 1 + return output + + def to_host(value): + if hasattr(value, "__cuda_array_interface__"): + counts["trainer_device_to_host_calls"] += 1 + return original_host(value) + + trainer.fit_tree_gpu_native = native + DistributionObjective.step = step + trainer._to_host = to_host + records = [] + previous_params = None + try: + with ob.backend_context(backend): + for phase in ("first_fit", "repeat_fit"): + counts = dict( + native_tree_calls=0, objective_calls=0, trainer_device_to_host_calls=0 + ) + model = ob.NaturalBoostNormal(**CONFIG, random_state=seed) + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + sync() + started = time.perf_counter() + model.fit( + X_train, + y_train, + **({"eval_set": [(X_val, y_val)]} if mode == "eval" else {}), + ) + sync() + fit_s = time.perf_counter() - started + fit_counts = counts.copy() + started = time.perf_counter() + params = model.predict_params(X_test) + sync() + predict_s = time.perf_counter() - started + fallback = [str(w.message) for w in caught if "fallback" in str(w.message).lower()] + record = { + "phase": phase, + "fit_s": fit_s, + "predict_params_s": predict_s, + "metrics": normal_metrics(y_test, params), + "fit_path": fit_counts, + "fallback_warnings": fallback, + "eval_history": model.evals_result_, + "prediction_sha256": hashlib.sha256( + b"".join( + np.asarray(params[k], dtype="= '3.11' \ + --hash=sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b \ + --hash=sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae \ + --hash=sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3 \ + --hash=sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0 \ + --hash=sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b \ + --hash=sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa \ + --hash=sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28 \ + --hash=sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e \ + --hash=sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017 \ + --hash=sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41 \ + --hash=sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e \ + --hash=sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63 \ + --hash=sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9 \ + --hash=sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8 \ + --hash=sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff \ + --hash=sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7 \ + --hash=sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139 \ + --hash=sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4 \ + --hash=sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748 \ + --hash=sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952 \ + --hash=sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd \ + --hash=sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b \ + --hash=sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce \ + --hash=sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f \ + --hash=sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5 \ + --hash=sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42 \ + --hash=sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7 \ + --hash=sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248 \ + --hash=sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e \ + --hash=sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3 \ + --hash=sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b \ + --hash=sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e \ + --hash=sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0 \ + --hash=sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa \ + --hash=sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a \ + --hash=sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5 \ + --hash=sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d \ + --hash=sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4 \ + --hash=sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c \ + --hash=sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52 \ + --hash=sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5 \ + --hash=sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d \ + --hash=sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1 \ + --hash=sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c \ + --hash=sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18 \ + --hash=sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7 \ + --hash=sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188 \ + --hash=sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218 \ + --hash=sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2 \ + --hash=sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903 \ + --hash=sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c \ + --hash=sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c \ + --hash=sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234 \ + --hash=sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82 \ + --hash=sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39 \ + --hash=sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf \ + --hash=sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20 \ + --hash=sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946 \ + --hash=sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0 \ + --hash=sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9 \ + --hash=sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff \ + --hash=sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad \ + --hash=sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227 \ + --hash=sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10 \ + --hash=sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e \ + --hash=sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf \ + --hash=sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769 \ + --hash=sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310 \ + --hash=sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425 \ + --hash=sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013 \ + --hash=sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c \ + --hash=sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb \ + --hash=sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d \ + --hash=sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520 +packaging==25.0 \ + --hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 \ + --hash=sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f +pluggy==1.6.0 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 +pygments==2.19.2 \ + --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \ + --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b +pytest==9.0.2 \ + --hash=sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b \ + --hash=sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11 +pytest-cov==7.0.0 \ + --hash=sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1 \ + --hash=sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 +pytest-xdist==3.8.0 \ + --hash=sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88 \ + --hash=sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1 +scipy==1.15.3 ; python_full_version < '3.11' \ + --hash=sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477 \ + --hash=sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c \ + --hash=sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723 \ + --hash=sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730 \ + --hash=sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539 \ + --hash=sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb \ + --hash=sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6 \ + --hash=sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594 \ + --hash=sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92 \ + --hash=sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82 \ + --hash=sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49 \ + --hash=sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759 \ + --hash=sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba \ + --hash=sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982 \ + --hash=sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8 \ + --hash=sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65 \ + --hash=sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4 \ + --hash=sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e \ + --hash=sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed \ + --hash=sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c \ + --hash=sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5 \ + --hash=sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5 \ + --hash=sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019 \ + --hash=sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e \ + --hash=sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1 \ + --hash=sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889 \ + --hash=sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca \ + --hash=sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825 \ + --hash=sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9 \ + --hash=sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62 \ + --hash=sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb \ + --hash=sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b \ + --hash=sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13 \ + --hash=sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb \ + --hash=sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40 \ + --hash=sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c \ + --hash=sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253 \ + --hash=sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb \ + --hash=sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f \ + --hash=sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163 \ + --hash=sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45 \ + --hash=sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7 \ + --hash=sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11 \ + --hash=sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf \ + --hash=sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e \ + --hash=sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126 +scipy==1.16.3 ; python_full_version >= '3.11' \ + --hash=sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2 \ + --hash=sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb \ + --hash=sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a \ + --hash=sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203 \ + --hash=sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304 \ + --hash=sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959 \ + --hash=sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a \ + --hash=sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d \ + --hash=sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe \ + --hash=sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562 \ + --hash=sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb \ + --hash=sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9 \ + --hash=sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc \ + --hash=sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d \ + --hash=sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686 \ + --hash=sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97 \ + --hash=sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353 \ + --hash=sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2 \ + --hash=sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876 \ + --hash=sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78 \ + --hash=sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc \ + --hash=sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119 \ + --hash=sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9 \ + --hash=sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f \ + --hash=sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135 \ + --hash=sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234 \ + --hash=sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1 \ + --hash=sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88 \ + --hash=sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc \ + --hash=sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6 \ + --hash=sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6 \ + --hash=sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22 \ + --hash=sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511 \ + --hash=sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079 \ + --hash=sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184 \ + --hash=sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c \ + --hash=sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40 \ + --hash=sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2 \ + --hash=sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0 \ + --hash=sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e \ + --hash=sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800 \ + --hash=sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4 \ + --hash=sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657 \ + --hash=sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005 \ + --hash=sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70 \ + --hash=sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07 \ + --hash=sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e \ + --hash=sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c \ + --hash=sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc \ + --hash=sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7 \ + --hash=sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146 \ + --hash=sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733 \ + --hash=sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c \ + --hash=sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d \ + --hash=sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6 \ + --hash=sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d \ + --hash=sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26 \ + --hash=sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8 \ + --hash=sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa \ + --hash=sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b \ + --hash=sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d +tomli==2.3.0 ; python_full_version <= '3.11' \ + --hash=sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456 \ + --hash=sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845 \ + --hash=sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999 \ + --hash=sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0 \ + --hash=sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878 \ + --hash=sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf \ + --hash=sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3 \ + --hash=sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be \ + --hash=sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52 \ + --hash=sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b \ + --hash=sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67 \ + --hash=sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549 \ + --hash=sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba \ + --hash=sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22 \ + --hash=sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c \ + --hash=sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f \ + --hash=sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6 \ + --hash=sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba \ + --hash=sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45 \ + --hash=sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f \ + --hash=sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77 \ + --hash=sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606 \ + --hash=sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441 \ + --hash=sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0 \ + --hash=sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f \ + --hash=sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530 \ + --hash=sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05 \ + --hash=sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8 \ + --hash=sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005 \ + --hash=sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879 \ + --hash=sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae \ + --hash=sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc \ + --hash=sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b \ + --hash=sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b \ + --hash=sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e \ + --hash=sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf \ + --hash=sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac \ + --hash=sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8 \ + --hash=sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b \ + --hash=sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf \ + --hash=sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463 \ + --hash=sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876 +typing-extensions==4.15.0 ; python_full_version < '3.11' \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 diff --git a/benchmarks/foundation/runner.py b/benchmarks/foundation/runner.py new file mode 100644 index 0000000..f1433fc --- /dev/null +++ b/benchmarks/foundation/runner.py @@ -0,0 +1,172 @@ +"""Validate returned smoke evidence; usable offline without Modal installed.""" + +import json +import math +import sys +import xml.etree.ElementTree as ET +from pathlib import Path + +REQUIRED_TESTS = {"test_device_interop", "test_normal_gpu_fit"} + + +def validate_result(manifest, result): + if result.get("returncode") != 0 or result.get("timed_out", True): + raise ValueError("Remote test process failed or timed out") + if manifest.get("source_dirty") is not False: + raise ValueError("Source must be committed and clean") + for key in ("wheel_sha256", "source_sha"): + if not manifest.get(key) or result.get(key) != manifest[key]: + raise ValueError(f"Provenance mismatch: {key}") + try: + root = ET.fromstring(result.get("junit", "")) + except ET.ParseError as exc: + raise ValueError("Missing or invalid JUnit report") from exc + required = set(REQUIRED_TESTS) + suite = manifest.get("suite", "smoke") + if suite in ("correctness", "boundaries", "baseline"): + required.update({"test_weighted_newton", "test_weighted_distribution[normal]", "test_weighted_distribution[poisson]"}) + elif suite in ("histograms", "splits", "leaves", "builder", "trainer"): + required.add("test_batch_histogram_device_oracle") + if suite in ("splits", "leaves", "builder", "trainer"): + required.add("test_batch_split_routing_oracle") + if suite in ("leaves", "builder", "trainer"): + required.add("test_batch_leaf_rule_oracle") + if suite in ("builder", "trainer"): + required.add("test_levelwise_builder_device_oracle") + if suite == "trainer": + required.add("test_strict_extension_trainer") + elif suite == "extensions": + required.add("test_installed_gpu_extensions") + elif suite in ("value", "value_profile"): + required.add("test_value_matrix") + elif suite != "smoke": + raise ValueError("Unknown evidence suite") + if suite in ("boundaries", "baseline"): + required.update({"test_visible_fallback[custom]", "test_visible_fallback[exposure]", "test_visible_fallback[generic]", "test_device_error_rolls_back", "test_device_sampling_preflight[subsample]", "test_device_sampling_preflight[colsample_bytree]", "test_eval_callback_persistence[normal]", "test_eval_callback_persistence[poisson]"}) + if suite == "baseline": + required.add("test_baseline_matrix") + cases = list(root.iter("testcase")) + names = [case.get("name") for case in cases] + if len(names) != len(required) or set(names) != required: + raise ValueError("Required GPU cases missing or duplicated") + if any(list(root.iter(tag)) for tag in ("failure", "error", "skipped")): + raise ValueError("GPU cases failed, errored, or skipped") + env = result.get("environment", {}) + checks = result.get("checks", {}) + if not env.get("cuda_available") or not env.get("gpu_name"): + raise ValueError("No real CUDA environment evidence") + if not ( + checks.get("interop") is True + and checks.get("native_tree_calls") == 4 + and checks.get("device_objective_calls") == 2 + and checks.get("installed_files_verified", 0) > 0 + and checks.get("dataset_sha256") + ): + raise ValueError("Missing installed-wheel/device-path checks (possible fallback)") + if suite in ("histograms", "splits", "leaves", "builder", "trainer"): + batch = checks.get("batch_histograms", {}) + if batch.get("device_arrays") is not True or batch.get("legacy_download_wrappers_blocked") is not True or len(batch.get("cases", [])) != 3: + raise ValueError("Missing batch histogram device/oracle checks") + if suite in ("splits", "leaves", "builder", "trainer"): + split = checks.get("batch_splits", {}) + if split.get("device_arrays") is not True or split.get("routed_child_oracle") is not True or split.get("exact_ties_and_gain_boundary") is not True or len(split.get("cases", [])) != 4: + raise ValueError("Missing split/routing oracle checks") + if suite in ("leaves", "builder", "trainer"): + leaf = checks.get("batch_leaves", {}) + if leaf.get("device_arrays") is not True or leaf.get("row_sum_oracle") is not True or leaf.get("bounded_changes_next_gradient") is not True or len(leaf.get("cases", [])) != 3 or len(leaf.get("two_rounds", [])) != 2: + raise ValueError("Missing leaf rule/reduction oracle checks") + if suite in ("builder", "trainer"): + builder = checks.get("levelwise_builder", {}) + if builder.get("device_cache_survives_owner_release") is not True or builder.get("cpu_load_prediction") is not True or builder.get("compact_transfer_calls") != 85 or len(builder.get("two_channel_cases", [])) != 4: + raise ValueError("Missing level-wise builder evidence") + if suite == "trainer": + execution = checks.get("strict_extension_trainer", {}) + if not all(execution.get(k) is True for k in ("actual_fit", "legacy_dispatch_blocked", "rollback", "cpu_load_prediction")) or execution.get("compact_transfer_calls") != 40 or len(execution.get("cases", [])) != 2 or len(execution.get("adapter_cases", [])) != 4 or execution.get("additional_invalid_statistics") != 3: + raise ValueError("Missing strict extension trainer evidence") + if suite == "extensions": + packages = checks.get("installed_gpu_extensions", {}) + uninstall = checks.get("extension_uninstall", {}) + if (len(manifest.get("extension_wheels", {})) != 2 or not manifest.get("extension_sources") + or packages.get("math_oracle") is not True + or packages.get("clipping_changes_next_gradient") is not True + or packages.get("schedule_changes_prediction") is not True + or packages.get("compact_transfer_calls") != 160 + or len(packages.get("cases", [])) != 8 + or packages.get("models_saved") != 9 + or uninstall.get("uninstall_returncode") != 0 + or uninstall.get("inference_returncode") != 0): + raise ValueError("Missing installed GPU extension conformance") + installed = packages.get("installed", {}) + expected_cells = {(n, b, s) for n in (16, 4097) for b in (False, True) for s in (False, True)} + cells = {(c.get("samples"), c.get("bounded"), c.get("scheduled")) for c in packages["cases"]} + if (set(installed) != {"normal_fisher", "bounded_leaves"} + or any(v.get("verified_python_files", 0) < 1 or "site-packages" not in v.get("path", "") for v in installed.values()) + or cells != expected_cells or packages.get("demo", {}).get("device") != "cuda"): + raise ValueError("Missing installed package or combination evidence") + inference = json.loads(uninstall.get("inference_stdout", "{}")) + if inference != {"extensions_absent": True, "exact_cpu_roundtrips": 9}: + raise ValueError("Missing plugin-free CPU inference evidence") + if suite in ("value", "value_profile"): + from benchmarks.foundation.value_protocol import summarize, validate_profiles + + cells = checks.get("value_cells", []) + if suite == "value_profile": + if not manifest.get("profile_parent", {}).get("results_sha256") or checks.get("profile_quality_matches_parent") is not True: + raise ValueError("Missing original timing reference") + validate_profiles(cells, profile_only=True) + else: + expected_summary = summarize(cells, checks.get("frozen_baseline_cells", [])) + validate_profiles(cells) + if checks.get("value_summary") != expected_summary: + raise ValueError("Value summary disagrees with raw evidence") + if suite == "baseline": + validate_baseline(checks.get("baseline_cells", [])) + + +def validate_baseline(cells): + expected = {(seed, mode, backend) for seed in (0, 1, 2) for mode in ("resident", "eval") for backend in ("cpu", "cuda")} + if len(cells) != len(expected): + raise ValueError("Incomplete baseline matrix") + indexed = {(c.get("seed"), c.get("mode"), c.get("backend")): c for c in cells} + if set(indexed) != expected: + raise ValueError("Missing or duplicated baseline cells") + for (_, _, backend), cell in indexed.items(): + records = cell.get("records", []) + if [r.get("phase") for r in records] != ["first_fit", "repeat_fit"]: + raise ValueError("Missing first/repeated fit") + for r in records: + if r.get("fallback_warnings") != []: + raise ValueError("Baseline fallback") + path = r.get("fit_path", {}) + if path.get("objective_calls") != 30 or path.get("native_tree_calls") != (60 if backend == "cuda" else 0): + raise ValueError("Baseline device path mismatch") + for name in ("fit_s", "predict_params_s"): + if not math.isfinite(r.get(name, float("nan"))) or r[name] <= 0: + raise ValueError("Invalid baseline timing") + for name in ("nll", "crps", "coverage90"): + if not math.isfinite(r.get("metrics", {}).get(name, float("nan"))): + raise ValueError("Invalid baseline metric") + for seed in (0, 1, 2): + for mode in ("resident", "eval"): + a, b = (indexed[seed, mode, backend]["records"][1]["metrics"] for backend in ("cpu", "cuda")) + if (abs(b["nll"] - a["nll"]) > .01 * max(1, abs(a["nll"])) + or b["crps"] > a["crps"] * 1.01 + or abs(b["coverage90"] - a["coverage90"]) > .01): + raise ValueError("Baseline quality gate failed") + + +def main(): + directory = Path(sys.argv[1]) + try: + manifest = json.loads((directory / "manifest.json").read_text()) + result = json.loads((directory / "results.json").read_text()) + validate_result(manifest, result) + except (OSError, ValueError, TypeError) as exc: + print(str(exc), file=sys.stderr) + return 1 + print(f"Foundation {manifest.get('suite', 'smoke')} evidence passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/foundation/value_protocol.py b/benchmarks/foundation/value_protocol.py new file mode 100644 index 0000000..3dffdc1 --- /dev/null +++ b/benchmarks/foundation/value_protocol.py @@ -0,0 +1,158 @@ +"""Predeclared P7 comparison gates; failures are data, not missing evidence.""" + +import math +import statistics + +CONFIG = dict( + n_trees=30, + max_depth=3, + learning_rate=0.05, + n_bins=64, + min_child_weight=1.0, + reg_lambda=1.0, + subsample=1.0, + colsample_bytree=1.0, +) + +STRATEGIES = ("legacy_cpu", "legacy_cuda", "experimental_cuda", "extensions_cuda") + + +def summarize(cells, frozen): + expected = {(seed, name) for seed in (0, 1, 2) for name in STRATEGIES} + indexed = {(c["seed"], c["strategy"]): c for c in cells} + if len(cells) != len(expected) or set(indexed) != expected: + raise ValueError("Incomplete or duplicate value matrix") + frozen_index = {(c["seed"], c["backend"], c["mode"]): c for c in frozen} + if any((seed, "cuda", "resident") not in frozen_index for seed in (0, 1, 2)): + raise ValueError("Missing frozen reference") + comparisons = [] + for seed in (0, 1, 2): + old = next( + c + for c in frozen + if c["seed"] == seed and c["backend"] == "cuda" and c["mode"] == "resident" + )["records"][1]["metrics"] + for strategy in STRATEGIES: + cell = indexed[seed, strategy] + if len(cell.get("records", [])) != 4 or cell.get("error"): + raise ValueError("Missing first plus three warm fits") + for record in cell["records"]: + values = [ + record["fit_s"], + record["predict_s"], + *(record["metrics"][k] for k in ("nll", "crps", "coverage90")), + ] + if ( + any(not math.isfinite(v) for v in values) + or record["fit_s"] <= 0 + or record["predict_s"] <= 0 + ): + raise ValueError("Invalid timings/metrics") + if record["fallback_warnings"]: + raise ValueError("Unexpected fallback") + cell["warm_fit_median_s"] = statistics.median(r["fit_s"] for r in cell["records"][1:]) + cell["warm_predict_median_s"] = statistics.median( + r["predict_s"] for r in cell["records"][1:] + ) + legacy, candidate = (indexed[seed, s] for s in ("legacy_cuda", "experimental_cuda")) + + def quality(reference, actual): + return ( + abs(actual["nll"] - reference["nll"]) <= 0.01 * max(1, abs(reference["nll"])) + and actual["crps"] <= 1.01 * reference["crps"] + and abs(actual["coverage90"] - reference["coverage90"]) <= 0.01 + ) + + comparisons.append( + { + "seed": seed, + "quality_pass": all( + quality(a["metrics"], b["metrics"]) + for a, b in zip(legacy["records"], candidate["records"], strict=True) + ), + "legacy_matches_frozen_quality": all( + quality(old, a["metrics"]) for a in legacy["records"] + ), + "warm_fit_ratio": candidate["warm_fit_median_s"] / legacy["warm_fit_median_s"], + } + ) + medians = { + s: statistics.median(indexed[seed, s]["warm_fit_median_s"] for seed in (0, 1, 2)) + for s in STRATEGIES + } + ratio = medians["experimental_cuda"] / medians["legacy_cuda"] + return { + "per_seed": comparisons, + "warm_fit_medians_s": medians, + "default_fit_ratio": ratio, + "profiling_triggered": ratio > 1.2, + "quality_pass": all( + c["quality_pass"] and c["legacy_matches_frozen_quality"] for c in comparisons + ), + "performance_budget_pass": ratio <= 1.2, + "scope": "resident fit only; eval/callbacks unsupported on strict GPU; T4 seconds are not billed dollars", + } + + +def validate_profiles(cells, *, profile_only=False): + """Reject a green matrix without the promised separate profile evidence.""" + if profile_only and ( + len(cells) != 4 + or {(c["seed"], c["strategy"]) for c in cells} != {(0, s) for s in STRATEGIES} + ): + raise ValueError("Incomplete isolated profile matrix") + for cell in cells: + if ( + cell.get("config") != CONFIG + or cell.get("mode") != "resident" + or cell.get("split_sizes") != [12384, 4128, 4128] + ): + raise ValueError("Value configuration changed") + expected_phases = ( + ["untimed_warmup"] if profile_only else ["process_first", "warm_1", "warm_2", "warm_3"] + ) + if [r.get("phase") for r in cell["records"]] != expected_phases: + raise ValueError("Value repetition order changed") + profile = cell.get("profile", {}) + if ( + not profile.get("top_host_functions") + or not math.isfinite(profile.get("wall_s", float("nan"))) + or profile["wall_s"] <= 0 + ): + raise ValueError("Missing separate profile") + if profile_only and ( + profile.get("isolated_host_profile") is not True + or not profile.get("synchronized_inclusive_timers") + ): + raise ValueError("Missing isolated host profile") + expected_device = "cpu" if cell["strategy"] == "legacy_cpu" else "cuda" + if any(r.get("actual_device") != expected_device for r in cell["records"]): + raise ValueError("Unexpected execution device") + if expected_device == "cuda": + counts = { + (r["file"], r["function"]): r["calls"] for r in profile.get("path_functions", []) + } + if cell["strategy"] == "legacy_cuda": + if ( + counts.get(("_tree.py", "fit_tree_gpu_native")) != 60 + or counts.get(("_objectives.py", "step")) != 30 + ): + raise ValueError("Missing native CUDA path") + elif ( + counts.get(("_device.py", "step")) != 30 + or counts.get(("_levelwise.py", "build")) != 60 + or counts.get(("_tree.py", "fit_tree_gpu_native"), 0) + ): + raise ValueError("Missing strict CUDA path") + memory = profile.get("memory", {}) + if memory.get("errors") != [] or memory.get("samples", 0) < 2: + raise ValueError("Missing sampled CUDA memory") + initial, peak, total = ( + memory.get(k, -1) + for k in ("initial_used_bytes", "sampled_peak_used_bytes", "total_bytes") + ) + if ( + not (0 <= initial <= peak <= total) + or memory.get("sampled_peak_delta_bytes") != peak - initial + ): + raise ValueError("Invalid sampled CUDA memory") diff --git a/benchmarks/foundation/value_worker.py b/benchmarks/foundation/value_worker.py new file mode 100644 index 0000000..14cd6c6 --- /dev/null +++ b/benchmarks/foundation/value_worker.py @@ -0,0 +1,284 @@ +"""Fresh-process P7 cell; profiling is a separate, untimed fifth fit.""" + +import argparse +import cProfile +import hashlib +import json +import pstats +import shutil +import threading +import time +import warnings +from pathlib import Path + +import numpy as np + +if __package__: + from .baseline_worker import CONFIG, normal_metrics + from .dataset import load_housing, split_indices +else: + from baseline_worker import CONFIG, normal_metrics + from dataset import load_housing, split_indices + + +def run_cell(strategy, seed, archive, output=None, profile_only=False): + import openboost as ob + from openboost.experimental import Booster, DistributionObjectiveAdapter, TrainerConfig + + backend = "cpu" if strategy == "legacy_cpu" else "cuda" + X, y = load_housing(archive) + train, val, test = split_indices(len(y), seed) + X_train, y_train, X_test, y_test = X[train], y[train], X[test], y[test] + objective = None + extra = {} + if strategy == "experimental_cuda": + objective = DistributionObjectiveAdapter("normal", natural=True) + elif strategy == "extensions_cuda": + from bounded_leaves import BoundedNewton + from normal_fisher import ChannelDecay, NormalFisher + + from openboost.experimental import LevelWiseBuilder + + objective = NormalFisher() + extra = dict( + tree_builder=LevelWiseBuilder(leaf_rule=BoundedNewton(0.5)), + step_schedule=ChannelDecay(tau=1), + ) + + def create(): + if objective is None: + return ob.NaturalBoostNormal(**CONFIG, random_state=seed) + return Booster( + objective=objective, + device=backend, + config=TrainerConfig(**CONFIG, random_state=seed), + **extra, + ) + + def sync(): + if backend == "cuda": + from numba import cuda + + cuda.synchronize() + + def predict(model): + if objective is None: + return model.predict_params(X_test) + params = objective.constrain(model.predict_raw(X_test)) + return ( + dict(loc=params["mu"], scale=params["sigma"]) + if strategy == "extensions_cuda" + else params + ) + + records = [] + result = dict( + strategy=strategy, + seed=seed, + config=CONFIG, + mode="resident", + split_sizes=[len(train), len(val), len(test)], + records=records, + ) + previous = None + with ob.backend_context(backend): + phases = ( + ("untimed_warmup",) if profile_only else ("process_first", "warm_1", "warm_2", "warm_3") + ) + for phase in phases: + model = create() + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + sync() + started = time.perf_counter() + model.fit(X_train, y_train) + sync() + fit_s = time.perf_counter() - started + started = time.perf_counter() + params = predict(model) + sync() + predict_s = time.perf_counter() - started + records.append( + dict( + phase=phase, + fit_s=fit_s, + predict_s=predict_s, + metrics=normal_metrics(y_test, params), + fallback_warnings=[ + str(w.message) for w in caught if "fallback" in str(w.message).lower() + ], + prediction_sha256=hashlib.sha256( + b"".join( + np.asarray(params[k], dtype=" \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T080803Z-c5ced00e/manifest.json b/benchmarks/results/foundation/20260905T080803Z-c5ced00e/manifest.json new file mode 100644 index 0000000..d91706c --- /dev/null +++ b/benchmarks/results/foundation/20260905T080803Z-c5ced00e/manifest.json @@ -0,0 +1,46 @@ +{ + "schema_version": 1, + "source_sha": "3101a4486034ff1848e764413ef50751a6b29678", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "83f41ea79ef6f5af2c61b662570deba29292f667788d81776423371fae4a6284", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_smoke" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T080803Z-c5ced00e", + "modal_image_id": "im-r5q1SNasWZ2u2zWnaj3pn5", + "actual_command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_smoke" + ] +} diff --git a/benchmarks/results/foundation/20260905T080803Z-c5ced00e/results.json b/benchmarks/results/foundation/20260905T080803Z-c5ced00e/results.json new file mode 100644 index 0000000..67ea3fe --- /dev/null +++ b/benchmarks/results/foundation/20260905T080803Z-c5ced00e/results.json @@ -0,0 +1,90 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784189440, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "numba-cuda": "0.27.0", + "coverage": "7.13.1", + "pytest-xdist": "3.8.0", + "iniconfig": "2.3.0", + "cuda-pathfinder": "1.4.0", + "llvmlite": "0.46.0", + "execnet": "2.1.2", + "cuda-core": "0.6.0", + "pip": "23.3.2", + "numba": "0.63.1", + "scipy": "1.16.3", + "pytest": "9.0.2", + "openboost": "1.0.0rc1", + "Pygments": "2.19.2", + "cupy-cuda12x": "13.6.0", + "pytest-cov": "7.0.0", + "cuda-bindings": "13.1.1", + "fastrlock": "0.8.3", + "numpy": "2.3.5", + "packaging": "25.0", + "pluggy": "1.6.0", + "setuptools": "69.0.3", + "joblib": "1.5.3", + "aiohttp": "3.12.7", + "hpack": "4.1.0", + "cbor2": "5.7.0", + "protobuf": "6.31.1", + "aiohappyeyeballs": "2.6.1", + "yarl": "1.20.0", + "grpclib": "0.4.8", + "hyperframe": "6.1.0", + "h2": "4.2.0", + "attrs": "25.3.0", + "typing_extensions": "4.13.2", + "idna": "3.10", + "frozenlist": "1.6.0", + "multidict": "6.4.4", + "propcache": "0.3.1", + "certifi": "2025.4.26", + "aiosignal": "1.3.2" + }, + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "3101a4486034ff1848e764413ef50751a6b29678", + "wheel_sha256": "83f41ea79ef6f5af2c61b662570deba29292f667788d81776423371fae4a6284", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": ".. [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n2 passed, 17 warnings in 9.16s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 37, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2 + }, + "remote_function_wall_s": 14.661769499, + "timing_scope": "smoke execution including environment checks/JIT; not a performance benchmark or billed duration" +} diff --git a/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/README.md b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/README.md new file mode 100644 index 0000000..3adcdb0 --- /dev/null +++ b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/README.md @@ -0,0 +1,38 @@ +# P2 weighted CUDA correctness: fixed source + +- Source: `8609f70dd5f3cfb030d76f1358e6b53f966e0e0d`, clean. +- Wheel SHA-256: `40a5ec42661eba6f72bf340147f29d0fd2ffed37877e77b6981d78cd1d0f888b`. +- Real Modal T4: **5 passed, 0 skipped**, pytest 9.97 seconds. +- Remote function wall time: 15.65 seconds, excluding image/startup; not billed + duration or a performance comparison. + +Fixed-bin native weighted Hessian sums are `[7, 10]`, matching CPU. Analytic +Newton leaves are `2.625` and `-1.81818187`; CPU and GPU one-round raw scores +match exactly. Three-round weighted Normal and Poisson raw scores also match +exactly on these fixtures, with matching NLL (2.24100208 and 2.19981337). +Gradient comparisons and root split assertions pass. Tests include zero and +nonuniform positive sample weights. + +The suite also verifies 37 installed wheel Python files, device interop, +two device objective calls and four native tree calls in the smoke fixture. +See `results.json` for environment, test output and numeric checks; `manifest.json` +for input hashes and exact CLI; `junit.xml` for mandatory-case completion. +Weighted fixtures are fully specified in the hash-pinned `test_correctness.py`; +the manifest's dataset field describes the additional smoke fixture only. + +Reproduce from this source commit in a clean checkout: + +```bash +uv run --no-sync python -m benchmarks.foundation.prepare --suite correctness +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_correctness +``` + +Offline validation: + +```bash +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T082025Z-ef9e0c4b +``` + +This establishes the narrow P2.1 weighted regression gate. It does not establish +real-data quality, general deep-tree parity, callback/evaluation transfer +boundaries, cross-device persistence or a cold/warm performance baseline. diff --git a/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/junit.xml b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/junit.xml new file mode 100644 index 0000000..cbd3658 --- /dev/null +++ b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/manifest.json b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/manifest.json new file mode 100644 index 0000000..e5ea089 --- /dev/null +++ b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/manifest.json @@ -0,0 +1,45 @@ +{ + "schema_version": 1, + "suite": "correctness", + "test_files": [ + "test_smoke.py", + "test_correctness.py" + ], + "source_sha": "8609f70dd5f3cfb030d76f1358e6b53f966e0e0d", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "40a5ec42661eba6f72bf340147f29d0fd2ffed37877e77b6981d78cd1d0f888b", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_correctness.py": "1a04571e4f3f22a05e2e4f5799967cbd1cfe21333a4e31a9f600be316fd19dcc" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_correctness" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T082025Z-ef9e0c4b", + "modal_image_id": "im-WP9cgmfa4obqGaYciQMJe3" +} diff --git a/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/results.json b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/results.json new file mode 100644 index 0000000..f6ba21a --- /dev/null +++ b/benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/results.json @@ -0,0 +1,139 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784181248, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "cuda-bindings": "13.1.1", + "numba": "0.63.1", + "pip": "23.3.2", + "pluggy": "1.6.0", + "numba-cuda": "0.27.0", + "coverage": "7.13.1", + "setuptools": "69.0.3", + "cuda-core": "0.6.0", + "cuda-pathfinder": "1.4.0", + "Pygments": "2.19.2", + "openboost": "1.0.0rc1", + "pytest-cov": "7.0.0", + "pytest": "9.0.2", + "llvmlite": "0.46.0", + "cupy-cuda12x": "13.6.0", + "packaging": "25.0", + "fastrlock": "0.8.3", + "scipy": "1.16.3", + "pytest-xdist": "3.8.0", + "numpy": "2.3.5", + "execnet": "2.1.2", + "joblib": "1.5.3", + "iniconfig": "2.3.0", + "certifi": "2025.4.26", + "aiosignal": "1.3.2", + "cbor2": "5.7.0", + "idna": "3.10", + "attrs": "25.3.0", + "protobuf": "6.31.1", + "hpack": "4.1.0", + "propcache": "0.3.1", + "hyperframe": "6.1.0", + "h2": "4.2.0", + "grpclib": "0.4.8", + "yarl": "1.20.0", + "frozenlist": "1.6.0", + "typing_extensions": "4.13.2", + "multidict": "6.4.4", + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7" + }, + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "8609f70dd5f3cfb030d76f1358e6b53f966e0e0d", + "wheel_sha256": "40a5ec42661eba6f72bf340147f29d0fd2ffed37877e77b6981d78cd1d0f888b", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_correctness.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "..... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\ntest_correctness.py: 3 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_correctness.py::test_weighted_newton\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\ntest_correctness.py::test_weighted_newton\n /usr/local/lib/python3.12/site-packages/openboost/_trainer.py:136: RuntimeWarning: CUDA objective fallback to CPU: objective capability; tree execution may still use CUDA\n return fit(model, *args, **kwargs)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n5 passed, 22 warnings in 9.97s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 37, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "weighted_newton": { + "cpu_hist_hess": [ + 7.0, + 10.0 + ], + "gpu_hist_hess": [ + 7.0, + 10.0 + ], + "expected_leaves": [ + 2.625, + -1.8181818723678589 + ], + "cpu_raw": [ + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859 + ], + "gpu_raw": [ + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859 + ] + }, + "weighted_normal": { + "nll": { + "cpu": 2.241002082824707, + "cuda": 2.241002082824707 + }, + "raw_max_abs_error": 0.0 + }, + "weighted_poisson": { + "nll": { + "cpu": 2.1998133659362793, + "cuda": 2.1998133659362793 + }, + "raw_max_abs_error": 0.0 + } + }, + "remote_function_wall_s": 15.654803316999999, + "timing_scope": "smoke execution including environment checks/JIT; not a performance benchmark or billed duration" +} diff --git a/benchmarks/results/foundation/20260905T082151Z-78e83b7e/README.md b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/README.md new file mode 100644 index 0000000..ad87615 --- /dev/null +++ b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/README.md @@ -0,0 +1,30 @@ +# P2 weighted CUDA correctness: pre-fix failure + +- Source: `502f37fd0cc38e215a2e888ed98167191a461a04`, clean detached checkout. +- Wheel SHA-256: `83f41ea79ef6f5af2c61b662570deba29292f667788d81776423371fae4a6284`. +- Real Modal T4: **3 failed, 2 passed, 0 skipped**, pytest 9.55 seconds. +- The CLI exited 1 and retained the failure artifacts, as required. + +This wheel is byte-identical to the earlier P1 wheel. Every test/config input +hash matches the explicitly authorized fixed-source bundle. The same suite +passes on [fixed source 8609f70](../20260905T082025Z-ef9e0c4b/README.md). + +The native constant-Hessian hint incorrectly produces histogram sums `[4, 4]` +instead of CPU's weighted `[7, 10]`. One-round GPU raw values become `0.42` and +`-0.4`, versus the analytic/CPU `0.2625` and `-0.18181819`. Weighted Normal and +Poisson CPU/CUDA raw maximum errors are 0.55151367 and 0.09349906 respectively. +All three weighted regression tests fail; both P1 smoke cases still pass. + +Lower training NLL in the incorrect path is not evidence of improved quality: +it used different Newton denominators and therefore different updates. This +is an algorithmic parity regression, not a held-out comparison. + +Reproduce in a clean checkout at this source commit: + +```bash +uv run --no-sync python -m benchmarks.foundation.prepare --suite correctness +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_correctness +``` + +The offline runner must reject this report. `manifest.json`, `results.json` +and `junit.xml` retain provenance, environment and the actual assertions. diff --git a/benchmarks/results/foundation/20260905T082151Z-78e83b7e/junit.xml b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/junit.xml new file mode 100644 index 0000000..6bd50eb --- /dev/null +++ b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/junit.xml @@ -0,0 +1,185 @@ +checks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...} +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x2b02b3423860> + + def test_weighted_newton(checks, monkeypatch): + from numba import cuda + + import openboost as ob + import openboost._trainer as trainer + from openboost._array import BinnedArray + from openboost._backends._cuda import _build_histogram_shared_kernel + from openboost._core._histogram import build_histogram + + bins = np.array([[0, 0, 0, 0, 1, 1, 1, 1]], dtype=np.uint8) + weights = np.array([0, 1, 2, 4, 0, 2, 3, 5], dtype=np.float32) + direction = np.array([-3, -3, -3, -3, 2, 2, 2, 2], dtype=np.float32) + grad, hess = direction * weights, weights.copy() + expected_leaves = np.array([21 / 8, -20 / 11], dtype=np.float32) + expected_raw = 0.1 * np.repeat(expected_leaves, 4) + binned = BinnedArray(bins, [np.array([0.5])], 1, 8, 'cpu') + + class FixedObjective: + channel_names = ['value'] + device_capable = False + unit_hessian = True + + def init_raw(self, *args): + return {'value': 0.0} + + def step(self, raw, y, sample_weight, extra): + return {'value': (direction * sample_weight, sample_weight.copy())} + + with ob.backend_context('cpu'): + hist_cpu = build_histogram(bins, grad, hess) + cpu = SimpleNamespace() + trainer.fit_boosting(cpu, FixedObjective(), binned, direction, + config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights) + raw_cpu = trainer.predict_raw(cpu, binned)['value'] + original = trainer.fit_tree_gpu_native + captured = {} + + def native(x, g, h, **kwargs): + hist = cuda.to_device(np.zeros((1, 1, 256, 2), dtype=np.float32)) + nodes = cuda.to_device(np.zeros(8, dtype=np.int32)) + _build_histogram_shared_kernel[(1, 1), 256]( + x, g, h, nodes, 0, 1, 0, hist, np.float32(kwargs['const_hess'])) + captured['hist'] = hist.copy_to_host()[0, 0] + return original(x, g, h, **kwargs) + + monkeypatch.setattr(trainer, 'fit_tree_gpu_native', native) + with ob.backend_context('cuda'): + gpu = SimpleNamespace() + trainer.fit_boosting(gpu, FixedObjective(), binned, direction, + config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights) + raw_gpu = trainer.predict_raw(gpu, binned)['value'] + checks['weighted_newton'] = { + 'cpu_hist_hess': hist_cpu[1][0, :2].tolist(), + 'gpu_hist_hess': captured['hist'][:2, 1].tolist(), + 'expected_leaves': expected_leaves.tolist(), + 'cpu_raw': raw_cpu.tolist(), 'gpu_raw': raw_gpu.tolist(), + } + np.testing.assert_allclose(raw_cpu, expected_raw, rtol=1e-6) + np.testing.assert_allclose(captured['hist'][:, 0], hist_cpu[0][0], atol=1e-6) +> np.testing.assert_allclose(captured['hist'][:, 1], hist_cpu[1][0], atol=1e-6) +E AssertionError: +E Not equal to tolerance rtol=1e-07, atol=1e-06 +E +E Mismatched elements: 2 / 256 (0.781%) +E Max absolute difference among violations: 6. +E Max relative difference among violations: 0.6 +E ACTUAL: array([4., 4., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., +E 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., +E 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,... +E DESIRED: array([ 7., 10., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., +E 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., +E 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,... + +test_correctness.py:70: AssertionErrordistribution = 'normal' +checks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...} + + @pytest.mark.parametrize('distribution', ['normal', 'poisson']) + def test_weighted_distribution(distribution, checks): + from numba import cuda + + import openboost as ob + from openboost._objectives import DistributionObjective + from openboost._trainer import predict_raw + + # Two distinct bins and no near-tied alternative splits. Reuse CPU bins. + X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0) + y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5 + weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16) + models, raws, metrics, gradients = {}, {}, {}, {} + with ob.backend_context('cpu'): + binned = ob.array(X) + for backend in ('cpu', 'cuda'): + with ob.backend_context(backend): + model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1) + model.fit(binned, y, sample_weight=weights) + models[backend] = model + raws[backend] = predict_raw(model, binned) + metrics[backend] = float(model.nll(binned, y)) + objective = DistributionObjective(model.distribution_, natural=True) + raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()} + if backend == 'cuda': + output = objective.step({k: cuda.to_device(v) for k, v in raw.items()}, + cuda.to_device(y), cuda.to_device(weights)) + gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()} + else: + gradients[backend] = objective.step(raw, y, weights) + checks[f'weighted_{distribution}'] = { + 'nll': metrics, + 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']), + } + for channel in raws['cpu']: + for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True): + np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6) +> np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6) +E AssertionError: +E Not equal to tolerance rtol=2e-05, atol=2e-06 +E +E Mismatched elements: 64 / 64 (100%) +E Max absolute difference among violations: 0.5515137 +E Max relative difference among violations: 0.08529427 +E ACTUAL: array([4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, +E 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, +E 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,... +E DESIRED: array([4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, +E 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, +E 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,... + +test_correctness.py:113: AssertionErrordistribution = 'poisson' +checks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...} + + @pytest.mark.parametrize('distribution', ['normal', 'poisson']) + def test_weighted_distribution(distribution, checks): + from numba import cuda + + import openboost as ob + from openboost._objectives import DistributionObjective + from openboost._trainer import predict_raw + + # Two distinct bins and no near-tied alternative splits. Reuse CPU bins. + X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0) + y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5 + weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16) + models, raws, metrics, gradients = {}, {}, {}, {} + with ob.backend_context('cpu'): + binned = ob.array(X) + for backend in ('cpu', 'cuda'): + with ob.backend_context(backend): + model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1) + model.fit(binned, y, sample_weight=weights) + models[backend] = model + raws[backend] = predict_raw(model, binned) + metrics[backend] = float(model.nll(binned, y)) + objective = DistributionObjective(model.distribution_, natural=True) + raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()} + if backend == 'cuda': + output = objective.step({k: cuda.to_device(v) for k, v in raw.items()}, + cuda.to_device(y), cuda.to_device(weights)) + gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()} + else: + gradients[backend] = objective.step(raw, y, weights) + checks[f'weighted_{distribution}'] = { + 'nll': metrics, + 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']), + } + for channel in raws['cpu']: + for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True): + np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6) +> np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6) +E AssertionError: +E Not equal to tolerance rtol=2e-05, atol=2e-06 +E +E Mismatched elements: 64 / 64 (100%) +E Max absolute difference among violations: 0.09349906 +E Max relative difference among violations: 0.04986759 +E ACTUAL: array([1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, +E 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, +E 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,... +E DESIRED: array([1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, +E 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, +E 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,... + +test_correctness.py:113: AssertionError \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T082151Z-78e83b7e/manifest.json b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/manifest.json new file mode 100644 index 0000000..f8b6200 --- /dev/null +++ b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/manifest.json @@ -0,0 +1,45 @@ +{ + "schema_version": 1, + "suite": "correctness", + "test_files": [ + "test_smoke.py", + "test_correctness.py" + ], + "source_sha": "502f37fd0cc38e215a2e888ed98167191a461a04", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "83f41ea79ef6f5af2c61b662570deba29292f667788d81776423371fae4a6284", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_correctness.py": "1a04571e4f3f22a05e2e4f5799967cbd1cfe21333a4e31a9f600be316fd19dcc" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_correctness" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T082151Z-78e83b7e", + "modal_image_id": "im-65nEoTMfNOOxFMaCutV41n" +} diff --git a/benchmarks/results/foundation/20260905T082151Z-78e83b7e/results.json b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/results.json new file mode 100644 index 0000000..bbeb9dc --- /dev/null +++ b/benchmarks/results/foundation/20260905T082151Z-78e83b7e/results.json @@ -0,0 +1,139 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 266818011136, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "Pygments": "2.19.2", + "numpy": "2.3.5", + "joblib": "1.5.3", + "execnet": "2.1.2", + "llvmlite": "0.46.0", + "scipy": "1.16.3", + "pytest": "9.0.2", + "numba-cuda": "0.27.0", + "cuda-core": "0.6.0", + "cupy-cuda12x": "13.6.0", + "pip": "23.3.2", + "coverage": "7.13.1", + "pytest-xdist": "3.8.0", + "cuda-bindings": "13.1.1", + "numba": "0.63.1", + "setuptools": "69.0.3", + "iniconfig": "2.3.0", + "pluggy": "1.6.0", + "packaging": "25.0", + "pytest-cov": "7.0.0", + "openboost": "1.0.0rc1", + "fastrlock": "0.8.3", + "cuda-pathfinder": "1.4.0", + "frozenlist": "1.6.0", + "aiosignal": "1.3.2", + "cbor2": "5.7.0", + "multidict": "6.4.4", + "idna": "3.10", + "propcache": "0.3.1", + "hyperframe": "6.1.0", + "h2": "4.2.0", + "yarl": "1.20.0", + "attrs": "25.3.0", + "certifi": "2025.4.26", + "grpclib": "0.4.8", + "aiohappyeyeballs": "2.6.1", + "hpack": "4.1.0", + "typing_extensions": "4.13.2", + "protobuf": "6.31.1", + "aiohttp": "3.12.7" + }, + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "502f37fd0cc38e215a2e888ed98167191a461a04", + "wheel_sha256": "83f41ea79ef6f5af2c61b662570deba29292f667788d81776423371fae4a6284", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_correctness.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 1, + "stdout": "..FFF [100%]\n=================================== FAILURES ===================================\n_____________________________ test_weighted_newton _____________________________\n\nchecks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\nmonkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x2b02b3423860>\n\n def test_weighted_newton(checks, monkeypatch):\n from numba import cuda\n \n import openboost as ob\n import openboost._trainer as trainer\n from openboost._array import BinnedArray\n from openboost._backends._cuda import _build_histogram_shared_kernel\n from openboost._core._histogram import build_histogram\n \n bins = np.array([[0, 0, 0, 0, 1, 1, 1, 1]], dtype=np.uint8)\n weights = np.array([0, 1, 2, 4, 0, 2, 3, 5], dtype=np.float32)\n direction = np.array([-3, -3, -3, -3, 2, 2, 2, 2], dtype=np.float32)\n grad, hess = direction * weights, weights.copy()\n expected_leaves = np.array([21 / 8, -20 / 11], dtype=np.float32)\n expected_raw = 0.1 * np.repeat(expected_leaves, 4)\n binned = BinnedArray(bins, [np.array([0.5])], 1, 8, 'cpu')\n \n class FixedObjective:\n channel_names = ['value']\n device_capable = False\n unit_hessian = True\n \n def init_raw(self, *args):\n return {'value': 0.0}\n \n def step(self, raw, y, sample_weight, extra):\n return {'value': (direction * sample_weight, sample_weight.copy())}\n \n with ob.backend_context('cpu'):\n hist_cpu = build_histogram(bins, grad, hess)\n cpu = SimpleNamespace()\n trainer.fit_boosting(cpu, FixedObjective(), binned, direction,\n config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights)\n raw_cpu = trainer.predict_raw(cpu, binned)['value']\n original = trainer.fit_tree_gpu_native\n captured = {}\n \n def native(x, g, h, **kwargs):\n hist = cuda.to_device(np.zeros((1, 1, 256, 2), dtype=np.float32))\n nodes = cuda.to_device(np.zeros(8, dtype=np.int32))\n _build_histogram_shared_kernel[(1, 1), 256](\n x, g, h, nodes, 0, 1, 0, hist, np.float32(kwargs['const_hess']))\n captured['hist'] = hist.copy_to_host()[0, 0]\n return original(x, g, h, **kwargs)\n \n monkeypatch.setattr(trainer, 'fit_tree_gpu_native', native)\n with ob.backend_context('cuda'):\n gpu = SimpleNamespace()\n trainer.fit_boosting(gpu, FixedObjective(), binned, direction,\n config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights)\n raw_gpu = trainer.predict_raw(gpu, binned)['value']\n checks['weighted_newton'] = {\n 'cpu_hist_hess': hist_cpu[1][0, :2].tolist(),\n 'gpu_hist_hess': captured['hist'][:2, 1].tolist(),\n 'expected_leaves': expected_leaves.tolist(),\n 'cpu_raw': raw_cpu.tolist(), 'gpu_raw': raw_gpu.tolist(),\n }\n np.testing.assert_allclose(raw_cpu, expected_raw, rtol=1e-6)\n np.testing.assert_allclose(captured['hist'][:, 0], hist_cpu[0][0], atol=1e-6)\n> np.testing.assert_allclose(captured['hist'][:, 1], hist_cpu[1][0], atol=1e-6)\nE AssertionError: \nE Not equal to tolerance rtol=1e-07, atol=1e-06\nE \nE Mismatched elements: 2 / 256 (0.781%)\nE Max absolute difference among violations: 6.\nE Max relative difference among violations: 0.6\nE ACTUAL: array([4., 4., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,...\nE DESIRED: array([ 7., 10., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,...\n\ntest_correctness.py:70: AssertionError\n______________________ test_weighted_distribution[normal] ______________________\n\ndistribution = 'normal'\nchecks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\n\n @pytest.mark.parametrize('distribution', ['normal', 'poisson'])\n def test_weighted_distribution(distribution, checks):\n from numba import cuda\n \n import openboost as ob\n from openboost._objectives import DistributionObjective\n from openboost._trainer import predict_raw\n \n # Two distinct bins and no near-tied alternative splits. Reuse CPU bins.\n X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0)\n y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5\n weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16)\n models, raws, metrics, gradients = {}, {}, {}, {}\n with ob.backend_context('cpu'):\n binned = ob.array(X)\n for backend in ('cpu', 'cuda'):\n with ob.backend_context(backend):\n model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1)\n model.fit(binned, y, sample_weight=weights)\n models[backend] = model\n raws[backend] = predict_raw(model, binned)\n metrics[backend] = float(model.nll(binned, y))\n objective = DistributionObjective(model.distribution_, natural=True)\n raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()}\n if backend == 'cuda':\n output = objective.step({k: cuda.to_device(v) for k, v in raw.items()},\n cuda.to_device(y), cuda.to_device(weights))\n gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()}\n else:\n gradients[backend] = objective.step(raw, y, weights)\n checks[f'weighted_{distribution}'] = {\n 'nll': metrics,\n 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']),\n }\n for channel in raws['cpu']:\n for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True):\n np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6)\n> np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6)\nE AssertionError: \nE Not equal to tolerance rtol=2e-05, atol=2e-06\nE \nE Mismatched elements: 64 / 64 (100%)\nE Max absolute difference among violations: 0.5515137\nE Max relative difference among violations: 0.08529427\nE ACTUAL: array([4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,\nE 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,\nE 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,...\nE DESIRED: array([4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,\nE 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,\nE 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,...\n\ntest_correctness.py:113: AssertionError\n_____________________ test_weighted_distribution[poisson] ______________________\n\ndistribution = 'poisson'\nchecks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\n\n @pytest.mark.parametrize('distribution', ['normal', 'poisson'])\n def test_weighted_distribution(distribution, checks):\n from numba import cuda\n \n import openboost as ob\n from openboost._objectives import DistributionObjective\n from openboost._trainer import predict_raw\n \n # Two distinct bins and no near-tied alternative splits. Reuse CPU bins.\n X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0)\n y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5\n weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16)\n models, raws, metrics, gradients = {}, {}, {}, {}\n with ob.backend_context('cpu'):\n binned = ob.array(X)\n for backend in ('cpu', 'cuda'):\n with ob.backend_context(backend):\n model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1)\n model.fit(binned, y, sample_weight=weights)\n models[backend] = model\n raws[backend] = predict_raw(model, binned)\n metrics[backend] = float(model.nll(binned, y))\n objective = DistributionObjective(model.distribution_, natural=True)\n raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()}\n if backend == 'cuda':\n output = objective.step({k: cuda.to_device(v) for k, v in raw.items()},\n cuda.to_device(y), cuda.to_device(weights))\n gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()}\n else:\n gradients[backend] = objective.step(raw, y, weights)\n checks[f'weighted_{distribution}'] = {\n 'nll': metrics,\n 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']),\n }\n for channel in raws['cpu']:\n for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True):\n np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6)\n> np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6)\nE AssertionError: \nE Not equal to tolerance rtol=2e-05, atol=2e-06\nE \nE Mismatched elements: 64 / 64 (100%)\nE Max absolute difference among violations: 0.09349906\nE Max relative difference among violations: 0.04986759\nE ACTUAL: array([1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,\nE 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,\nE 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,...\nE DESIRED: array([1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,\nE 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,\nE 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,...\n\ntest_correctness.py:113: AssertionError\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\ntest_correctness.py: 3 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_correctness.py::test_weighted_newton\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n=========================== short test summary info ============================\nFAILED test_correctness.py::test_weighted_newton - AssertionError: \nFAILED test_correctness.py::test_weighted_distribution[normal] - AssertionErr...\nFAILED test_correctness.py::test_weighted_distribution[poisson] - AssertionEr...\n3 failed, 2 passed, 21 warnings in 9.55s\n", + "stderr": "", + "junit": "checks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\nmonkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x2b02b3423860>\n\n def test_weighted_newton(checks, monkeypatch):\n from numba import cuda\n \n import openboost as ob\n import openboost._trainer as trainer\n from openboost._array import BinnedArray\n from openboost._backends._cuda import _build_histogram_shared_kernel\n from openboost._core._histogram import build_histogram\n \n bins = np.array([[0, 0, 0, 0, 1, 1, 1, 1]], dtype=np.uint8)\n weights = np.array([0, 1, 2, 4, 0, 2, 3, 5], dtype=np.float32)\n direction = np.array([-3, -3, -3, -3, 2, 2, 2, 2], dtype=np.float32)\n grad, hess = direction * weights, weights.copy()\n expected_leaves = np.array([21 / 8, -20 / 11], dtype=np.float32)\n expected_raw = 0.1 * np.repeat(expected_leaves, 4)\n binned = BinnedArray(bins, [np.array([0.5])], 1, 8, 'cpu')\n \n class FixedObjective:\n channel_names = ['value']\n device_capable = False\n unit_hessian = True\n \n def init_raw(self, *args):\n return {'value': 0.0}\n \n def step(self, raw, y, sample_weight, extra):\n return {'value': (direction * sample_weight, sample_weight.copy())}\n \n with ob.backend_context('cpu'):\n hist_cpu = build_histogram(bins, grad, hess)\n cpu = SimpleNamespace()\n trainer.fit_boosting(cpu, FixedObjective(), binned, direction,\n config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights)\n raw_cpu = trainer.predict_raw(cpu, binned)['value']\n original = trainer.fit_tree_gpu_native\n captured = {}\n \n def native(x, g, h, **kwargs):\n hist = cuda.to_device(np.zeros((1, 1, 256, 2), dtype=np.float32))\n nodes = cuda.to_device(np.zeros(8, dtype=np.int32))\n _build_histogram_shared_kernel[(1, 1), 256](\n x, g, h, nodes, 0, 1, 0, hist, np.float32(kwargs['const_hess']))\n captured['hist'] = hist.copy_to_host()[0, 0]\n return original(x, g, h, **kwargs)\n \n monkeypatch.setattr(trainer, 'fit_tree_gpu_native', native)\n with ob.backend_context('cuda'):\n gpu = SimpleNamespace()\n trainer.fit_boosting(gpu, FixedObjective(), binned, direction,\n config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights)\n raw_gpu = trainer.predict_raw(gpu, binned)['value']\n checks['weighted_newton'] = {\n 'cpu_hist_hess': hist_cpu[1][0, :2].tolist(),\n 'gpu_hist_hess': captured['hist'][:2, 1].tolist(),\n 'expected_leaves': expected_leaves.tolist(),\n 'cpu_raw': raw_cpu.tolist(), 'gpu_raw': raw_gpu.tolist(),\n }\n np.testing.assert_allclose(raw_cpu, expected_raw, rtol=1e-6)\n np.testing.assert_allclose(captured['hist'][:, 0], hist_cpu[0][0], atol=1e-6)\n> np.testing.assert_allclose(captured['hist'][:, 1], hist_cpu[1][0], atol=1e-6)\nE AssertionError: \nE Not equal to tolerance rtol=1e-07, atol=1e-06\nE \nE Mismatched elements: 2 / 256 (0.781%)\nE Max absolute difference among violations: 6.\nE Max relative difference among violations: 0.6\nE ACTUAL: array([4., 4., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,...\nE DESIRED: array([ 7., 10., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\nE 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,...\n\ntest_correctness.py:70: AssertionErrordistribution = 'normal'\nchecks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\n\n @pytest.mark.parametrize('distribution', ['normal', 'poisson'])\n def test_weighted_distribution(distribution, checks):\n from numba import cuda\n \n import openboost as ob\n from openboost._objectives import DistributionObjective\n from openboost._trainer import predict_raw\n \n # Two distinct bins and no near-tied alternative splits. Reuse CPU bins.\n X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0)\n y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5\n weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16)\n models, raws, metrics, gradients = {}, {}, {}, {}\n with ob.backend_context('cpu'):\n binned = ob.array(X)\n for backend in ('cpu', 'cuda'):\n with ob.backend_context(backend):\n model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1)\n model.fit(binned, y, sample_weight=weights)\n models[backend] = model\n raws[backend] = predict_raw(model, binned)\n metrics[backend] = float(model.nll(binned, y))\n objective = DistributionObjective(model.distribution_, natural=True)\n raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()}\n if backend == 'cuda':\n output = objective.step({k: cuda.to_device(v) for k, v in raw.items()},\n cuda.to_device(y), cuda.to_device(weights))\n gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()}\n else:\n gradients[backend] = objective.step(raw, y, weights)\n checks[f'weighted_{distribution}'] = {\n 'nll': metrics,\n 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']),\n }\n for channel in raws['cpu']:\n for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True):\n np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6)\n> np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6)\nE AssertionError: \nE Not equal to tolerance rtol=2e-05, atol=2e-06\nE \nE Mismatched elements: 64 / 64 (100%)\nE Max absolute difference among violations: 0.5515137\nE Max relative difference among violations: 0.08529427\nE ACTUAL: array([4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,\nE 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,\nE 4.580855, 4.580855, 4.580855, 4.580855, 4.580855, 4.580855,...\nE DESIRED: array([4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,\nE 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,\nE 4.328078, 4.328078, 4.328078, 4.328078, 4.328078, 4.328078,...\n\ntest_correctness.py:113: AssertionErrordistribution = 'poisson'\nchecks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 37, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\n\n @pytest.mark.parametrize('distribution', ['normal', 'poisson'])\n def test_weighted_distribution(distribution, checks):\n from numba import cuda\n \n import openboost as ob\n from openboost._objectives import DistributionObjective\n from openboost._trainer import predict_raw\n \n # Two distinct bins and no near-tied alternative splits. Reuse CPU bins.\n X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0)\n y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5\n weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16)\n models, raws, metrics, gradients = {}, {}, {}, {}\n with ob.backend_context('cpu'):\n binned = ob.array(X)\n for backend in ('cpu', 'cuda'):\n with ob.backend_context(backend):\n model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1)\n model.fit(binned, y, sample_weight=weights)\n models[backend] = model\n raws[backend] = predict_raw(model, binned)\n metrics[backend] = float(model.nll(binned, y))\n objective = DistributionObjective(model.distribution_, natural=True)\n raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()}\n if backend == 'cuda':\n output = objective.step({k: cuda.to_device(v) for k, v in raw.items()},\n cuda.to_device(y), cuda.to_device(weights))\n gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()}\n else:\n gradients[backend] = objective.step(raw, y, weights)\n checks[f'weighted_{distribution}'] = {\n 'nll': metrics,\n 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']),\n }\n for channel in raws['cpu']:\n for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True):\n np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6)\n> np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6)\nE AssertionError: \nE Not equal to tolerance rtol=2e-05, atol=2e-06\nE \nE Mismatched elements: 64 / 64 (100%)\nE Max absolute difference among violations: 0.09349906\nE Max relative difference among violations: 0.04986759\nE ACTUAL: array([1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,\nE 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,\nE 1.523059, 1.523059, 1.523059, 1.523059, 1.523059, 1.523059,...\nE DESIRED: array([1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,\nE 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,\nE 1.468088, 1.468088, 1.468088, 1.468088, 1.468088, 1.468088,...\n\ntest_correctness.py:113: AssertionError", + "checks": { + "installed_files_verified": 37, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "weighted_newton": { + "cpu_hist_hess": [ + 7.0, + 10.0 + ], + "gpu_hist_hess": [ + 4.0, + 4.0 + ], + "expected_leaves": [ + 2.625, + -1.8181818723678589 + ], + "cpu_raw": [ + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859 + ], + "gpu_raw": [ + 0.41999998688697815, + 0.41999998688697815, + 0.41999998688697815, + 0.41999998688697815, + -0.4000000059604645, + -0.4000000059604645, + -0.4000000059604645, + -0.4000000059604645 + ] + }, + "weighted_normal": { + "nll": { + "cpu": 2.241002082824707, + "cuda": 2.145698308944702 + }, + "raw_max_abs_error": 0.551513671875 + }, + "weighted_poisson": { + "nll": { + "cpu": 2.1998133659362793, + "cuda": 2.0862653255462646 + }, + "raw_max_abs_error": 0.0934990644454956 + } + }, + "remote_function_wall_s": 13.812369432, + "timing_scope": "smoke execution including environment checks/JIT; not a performance benchmark or billed duration" +} diff --git a/benchmarks/results/foundation/20260905T084129Z-2574e387/README.md b/benchmarks/results/foundation/20260905T084129Z-2574e387/README.md new file mode 100644 index 0000000..e370bec --- /dev/null +++ b/benchmarks/results/foundation/20260905T084129Z-2574e387/README.md @@ -0,0 +1,107 @@ +# P2 execution boundaries and real-data baseline + +Source `99621ae9e640be9c1144a015a39de43d3bf50ae9`, clean; wheel SHA-256 +`40a5ec42661eba6f72bf340147f29d0fd2ffed37877e77b6981d78cd1d0f888b`. + +**14 passed, 0 skipped**, pytest 104.58 s; remote function wall time 108.88 s. +The CLI and offline evidence validator both returned success. Raw records: +[manifest](manifest.json), [results](results.json), [JUnit](junit.xml). + +## Correctness and execution boundaries + +All five earlier smoke/weighted cases and eight new boundary cases passed: + +- Same-name custom distribution, exposure and generic-tree fallback are visible + and match CPU predictions on their controlled fixtures. +- A deliberate device-kernel error propagates and restores unfitted trainer + state; unsupported GPU row/column sampling fails before binning/updates. +- Normal and Poisson callback/eval values and unweighted CPU/CUDA predictions + match; GPU-save/CPU-load and CPU-save/GPU-load preserve predictions. +- The callback fixture downloads raw scores at the observed trainer boundary + once per round (3 times); the no-callback fixture has no such downloads. + +## Frozen dataset and quality + +California Housing, 20,640 rows, 8 features, target in units of 100,000 USD. +Original archive SHA-256: +`aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681`. +Float32 feature/target arrays SHA-256: +`34fc72e53a6329a89a9ae792e92f37853cea98adfbc6feea28cc08dc06f68ca1`. +All source, copied input, dataset and split hashes were independently checked. + +Seeds 0, 1, 2 use fixed 60/20/20 splits: 12,384 training, 4,128 validation, +4,128 test rows. Only training data fits the 64-bin feature boundaries. +Normal model: 30 rounds, depth 3, learning rate .05, min_child_weight=1, +reg_lambda=1 and no sampling. No target tuning or learned scaling. + +The following are held-out repeated-fit results without eval. Eval-mode CPU +metrics are identical; GPU eval-mode absolute differences from CPU are at most +1.34e-8 NLL, 1.12e-8 CRPS and zero coverage difference. Every seed/mode passes +the predeclared design gates; no threshold was changed after collection. + +| Seed | CPU NLL | CUDA NLL | CPU CRPS | CUDA CRPS | CPU/CUDA coverage90 | +| --- | ---: | ---: | ---: | ---: | ---: | +| 0 | 1.094473764 | 1.094473769 | 0.398590950 | 0.398590956 | 0.967781008 | +| 1 | 1.113434585 | 1.113434586 | 0.410066944 | 0.410066944 | 0.963905039 | +| 2 | 1.096392043 | 1.096392045 | 0.403819868 | 0.403819868 | 0.964389535 | + +CPU repeated predictions are bit-identical; maximum CUDA repeated-parameter +absolute difference is 4.768e-7, within the declared tolerance. These are +parity results for a fixed, untuned baseline. Nominal 90% intervals cover +96.39–96.78%, so this run does not establish good calibration or tuned quality. +Three seeds are not evidence of statistical significance. + +## End-to-end timing baseline + +Medians across the three seeds, seconds. Each backend/seed/mode uses a fresh +Python process and NUMBA_CACHE_DIR, then a first and repeated fit (12 processes, +24 fits total). Fits include binning, gradient math, copies, JIT compilation +and path-counting instrumentation. Test prediction includes binning and all +parameter channels. Imports, data loading, image build and container startup +are excluded. CUDA driver caches were not explicitly cleared: “first fit” +means process-first under this protocol, not a machine-cold timing. + +| Backend | Eval mode | First fit | Repeated fit | Prediction after repeated fit | +| --- | --- | ---: | ---: | ---: | +| CPU | none | 4.413173 | 2.401469 | 0.684316 | +| CUDA | none | 2.412475 | 0.145193 | 0.067331 | +| CPU | validation | 5.106433 | 3.026923 | 0.676538 | +| CUDA | validation | 2.450300 | 0.230342 | 0.016565 | + +Evaluation invokes tree prediction during fitting and populates per-tree GPU +array caches; no-eval native training updates raw scores directly. These modes +therefore reach test prediction with different cache states. The table retains +mode-specific timings. This is one bounded baseline run with small repeated +samples, not a general performance claim against another boosting library. +Future comparisons must use the same protocol and matched quality. + +All CUDA fits execute 30 device objective steps and 60 native trees without +fallback warnings. The partial trainer device-to-host counter is 0 without +eval and 60 with eval. It excludes compact tree conversion and backend-internal +copies and must not be interpreted as total PCIe traffic or zero transfers. + +## Environment and reproduction + +Tesla T4 15,360 MiB, driver 580.95.05; CuPy runtime 12.9, driver API 13.0, +pinned CUDA 12.4 toolkit base. Two requested CPU cores, 8 GiB requested memory, +thread counts fixed at 2. `/proc/cpuinfo` reports CPU model `unknown`; the +physical CPU model is therefore unavailable. Full OS, package and resource +metadata are in `results.json`. No peak-memory/scaling claim is made. + +From the tested source in a clean checkout: + +```bash +uv run --no-sync python -c 'from benchmarks.foundation.dataset import fetch; fetch("build/foundation_data/cal_housing.tgz")' +uv run --no-sync python -m benchmarks.foundation.prepare --suite baseline +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_baseline +``` + +Offline validation: + +```bash +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T084129Z-2574e387 +``` + +The single-T4 function has timeout 1800s, retry=0 and max_containers=1; pytest +is limited to 1740s and each matrix process to 150s. All 13 prerequisite cases +execute before the baseline matrix, with maxfail=1. diff --git a/benchmarks/results/foundation/20260905T084129Z-2574e387/junit.xml b/benchmarks/results/foundation/20260905T084129Z-2574e387/junit.xml new file mode 100644 index 0000000..00bcb00 --- /dev/null +++ b/benchmarks/results/foundation/20260905T084129Z-2574e387/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T084129Z-2574e387/manifest.json b/benchmarks/results/foundation/20260905T084129Z-2574e387/manifest.json new file mode 100644 index 0000000..7fb089b --- /dev/null +++ b/benchmarks/results/foundation/20260905T084129Z-2574e387/manifest.json @@ -0,0 +1,104 @@ +{ + "schema_version": 1, + "suite": "baseline", + "test_files": [ + "test_smoke.py", + "test_correctness.py", + "test_boundaries.py", + "test_baseline.py" + ], + "source_sha": "99621ae9e640be9c1144a015a39de43d3bf50ae9", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "40a5ec42661eba6f72bf340147f29d0fd2ffed37877e77b6981d78cd1d0f888b", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_correctness.py": "1a04571e4f3f22a05e2e4f5799967cbd1cfe21333a4e31a9f600be316fd19dcc", + "test_boundaries.py": "86de8e6aa2e5da128205847d2afee3adb17e6a6ce027a34a9f53cfcddce278ad", + "dataset.py": "cee302d02c1f82e54c19693fdbff8a7671955f816cccb6fc5902860414109e2a", + "baseline_worker.py": "3abb07c372d2fae3ffc8973ff1b1220d207302b9f04282514762eb907e363e88", + "housing.json": "d511bf1b61c6e515598a91cc468b9c860dcde60f9e1e7a3e2570e9d8e8c3bc5b", + "test_baseline.py": "d51c3a3b164bcb2296d7fdd8f5eb4f98a6f65ab1f9f18d7b6e1e8f77452b769a", + "cal_housing.tgz": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_baseline" + ], + "gpu": "T4", + "timeout_s": 1800, + "retries": 0, + "dataset": { + "name": "California Housing", + "source_url": "https://ndownloader.figshare.com/files/5976036", + "archive_sha256": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681", + "member": "CaliforniaHousing/cal_housing.data", + "features": [ + "MedInc", + "HouseAge", + "AveRooms", + "AveBedrms", + "Population", + "AveOccup", + "Latitude", + "Longitude" + ], + "shape": [ + 20640, + 8 + ], + "target_unit": "100,000 USD", + "arrays_sha256": "34fc72e53a6329a89a9ae792e92f37853cea98adfbc6feea28cc08dc06f68ca1", + "dtype": "little-endian float32", + "split": "numpy.default_rng(seed), permutation, 60/20/20", + "split_sha256": { + "0": [ + "7d07544c6c3fddd72d0cfc8d0aa0c19cf00250f1890420bdb10bd80efb0c9c04", + "5f6d9931d5a5239c7933da9bb62df2c631e79017658f185742db94f139be358a", + "877744733fe0213e716ca084333f39385c30654169f117f772c53a6af8bcff37" + ], + "1": [ + "99578d3ef329ef275fd41e3834df7dab763525f9374db203b6f748b783eed05a", + "a161a60cd85d1ec54cbfd590f6befdb58b1a9a695c635bfbb562b4064d265caf", + "039c4fc07f6596a3cfdf73cb55820de4baa13f93251ede4c215796f283d46256" + ], + "2": [ + "51ab6cbe0019b5bea9f9da8a81b3fafa874d8c992062f65ee8d1b09a3b7898a1", + "14509d20c7d87948ac6ea9060c476995583dd130af0ff928a9c24709e5ecf9d4", + "ff6426d360c75b637dd866cd73d9fd1b9fcf310a886dc05fbac0f80dcf256ec8" + ] + } + }, + "baseline_protocol": { + "seeds": [ + 0, + 1, + 2 + ], + "modes": [ + "resident", + "eval" + ], + "backends": [ + "cpu", + "cuda" + ], + "repeats_per_cell": 2, + "nll_abs_tolerance": "0.01 * max(1, abs(cpu_nll))", + "crps_max_ratio": 1.01, + "coverage90_abs_tolerance": 0.01 + }, + "run_id": "20260905T084129Z-2574e387", + "modal_image_id": "im-mL8DX2TBByzKpTYX2yK17J" +} diff --git a/benchmarks/results/foundation/20260905T084129Z-2574e387/results.json b/benchmarks/results/foundation/20260905T084129Z-2574e387/results.json new file mode 100644 index 0000000..e26391e --- /dev/null +++ b/benchmarks/results/foundation/20260905T084129Z-2574e387/results.json @@ -0,0 +1,1333 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784177152, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "scipy": "1.16.3", + "numba-cuda": "0.27.0", + "pluggy": "1.6.0", + "cuda-bindings": "13.1.1", + "fastrlock": "0.8.3", + "cuda-core": "0.6.0", + "pip": "23.3.2", + "coverage": "7.13.1", + "cupy-cuda12x": "13.6.0", + "numba": "0.63.1", + "joblib": "1.5.3", + "openboost": "1.0.0rc1", + "llvmlite": "0.46.0", + "cuda-pathfinder": "1.4.0", + "pytest-cov": "7.0.0", + "numpy": "2.3.5", + "pytest": "9.0.2", + "iniconfig": "2.3.0", + "packaging": "25.0", + "setuptools": "69.0.3", + "pytest-xdist": "3.8.0", + "execnet": "2.1.2", + "Pygments": "2.19.2", + "hyperframe": "6.1.0", + "aiohappyeyeballs": "2.6.1", + "idna": "3.10", + "yarl": "1.20.0", + "aiohttp": "3.12.7", + "typing_extensions": "4.13.2", + "protobuf": "6.31.1", + "attrs": "25.3.0", + "aiosignal": "1.3.2", + "cbor2": "5.7.0", + "certifi": "2025.4.26", + "hpack": "4.1.0", + "propcache": "0.3.1", + "frozenlist": "1.6.0", + "multidict": "6.4.4", + "h2": "4.2.0", + "grpclib": "0.4.8" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "99621ae9e640be9c1144a015a39de43d3bf50ae9", + "wheel_sha256": "40a5ec42661eba6f72bf340147f29d0fd2ffed37877e77b6981d78cd1d0f888b", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_correctness.py", + "test_boundaries.py", + "test_baseline.py", + "--junitxml=junit.xml", + "--maxfail=1" + ], + "timed_out": false, + "returncode": 0, + "stdout": ".............. [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\ntest_correctness.py: 3 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_correctness.py::test_weighted_newton\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\ntest_correctness.py::test_weighted_newton\n /usr/local/lib/python3.12/site-packages/openboost/_trainer.py:136: RuntimeWarning: CUDA objective fallback to CPU: objective capability; tree execution may still use CUDA\n return fit(model, *args, **kwargs)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n14 passed, 22 warnings in 104.58s (0:01:44)\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 37, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "weighted_newton": { + "cpu_hist_hess": [ + 7.0, + 10.0 + ], + "gpu_hist_hess": [ + 7.0, + 10.0 + ], + "expected_leaves": [ + 2.625, + -1.8181818723678589 + ], + "cpu_raw": [ + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859 + ], + "gpu_raw": [ + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + 0.26250001788139343, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859, + -0.1818181872367859 + ] + }, + "weighted_normal": { + "nll": { + "cpu": 2.241002082824707, + "cuda": 2.241002082824707 + }, + "raw_max_abs_error": 0.0 + }, + "weighted_poisson": { + "nll": { + "cpu": 2.1998133659362793, + "cuda": 2.1998133659362793 + }, + "raw_max_abs_error": 0.0 + }, + "fallback_custom": [ + "CUDA objective fallback to CPU: objective capability; tree execution may still use CUDA" + ], + "fallback_exposure": [ + "CUDA objective fallback to CPU: exposure offsets; tree execution may still use CUDA" + ], + "fallback_generic": [ + "CUDA native tree fallback to generic tree path (constraints or feature metadata)" + ], + "device_error_propagated": true, + "preflight_subsample": true, + "preflight_colsample_bytree": true, + "boundaries_normal": { + "callback_rounds": 3, + "raw_host_calls_with_callback": 3, + "bidirectional_persistence": true, + "eval_final_nll": 2.228015422821045 + }, + "boundaries_poisson": { + "callback_rounds": 3, + "raw_host_calls_with_callback": 3, + "bidirectional_persistence": true, + "eval_final_nll": 2.1877317428588867 + }, + "baseline_cells": [ + { + "backend": "cpu", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.397831454999999, + "predict_params_s": 0.6745909410000017, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4014690210000005, + "predict_params_s": 0.6882345490000006, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 3.2421392509999976, + "predict_params_s": 0.19642429199999967, + "metrics": { + "nll": 1.0944737563673146, + "crps": 0.39859095157938995, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "f30ad0f19907a419c4106ddb23b3ed388782751464b971fb3ae4e231ef84368c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14386336900000174, + "predict_params_s": 0.0673137920000002, + "metrics": { + "nll": 1.0944737689342998, + "crps": 0.39859095595416205, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "a0feedfd12a1e785bd4b8fe1a82bf04da2a069ece3b8c37d812db27ce3af2963", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.106432963000003, + "predict_params_s": 0.6825809110000023, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 3.026923318999998, + "predict_params_s": 0.6765377500000014, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.450299663999999, + "predict_params_s": 0.01670187599999906, + "metrics": { + "nll": 1.0944737574085481, + "crps": 0.39859095178535353, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "4ab822ab4020fe3163e0664f5c51b755060bffa203f544e45148aa66544d4aeb" + }, + { + "phase": "repeat_fit", + "fit_s": 0.22371609900000067, + "predict_params_s": 0.01638825799999921, + "metrics": { + "nll": 1.0944737612758777, + "crps": 0.3985909545795274, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991604804992676, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "794edadbc23c07f1590fc520e02046cda972173bd5c4fca1e3ca8566ca5dadb5", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.533779657000004, + "predict_params_s": 0.6762787880000047, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4129718869999977, + "predict_params_s": 0.6843157980000001, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.407129374, + "predict_params_s": 0.16393877899999865, + "metrics": { + "nll": 1.113434598491401, + "crps": 0.4100669511518961, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "9111616954b6fb780447600cf7aec943b45d3e575b16cf188d1e03fc0e81ae1b" + }, + { + "phase": "repeat_fit", + "fit_s": 0.16078772999999558, + "predict_params_s": 0.06733073400000222, + "metrics": { + "nll": 1.1134345862813968, + "crps": 0.4100669440537582, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "26d571b4fae82b7b516354f105c2e48465aabc290c649e17b1450ff0274c8319", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.032100342, + "predict_params_s": 0.6659178060000102, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 3.0121408979999984, + "predict_params_s": 0.6714607630000131, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.386929182000003, + "predict_params_s": 0.01657929299999239, + "metrics": { + "nll": 1.1134345775848313, + "crps": 0.41006693913527403, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "f0446a567cc7ce8929da5b47732e69a48ba8fd76640e21a1c83b260e6094f652" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23034155599999906, + "predict_params_s": 0.016565403999990735, + "metrics": { + "nll": 1.1134345986050809, + "crps": 0.41006695480534144, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.4206825494766235, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.242552638053894, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872674226760864, + 1.1751006841659546, + 1.1648061275482178, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "45b3c964270056fd4530962345d490834970cdb3fffb7002c7f743235f090923", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.4131726219999905, + "predict_params_s": 0.6596325430000007, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 2.385363081999998, + "predict_params_s": 0.6809927400000078, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.4124752700000016, + "predict_params_s": 0.1630236369999949, + "metrics": { + "nll": 1.0963920238037668, + "crps": 0.4038198588211825, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "29253dd5874532fcafc8c8c2bc392181213ae0ea9cbeef4d6297378ac1f3113c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14519270999998923, + "predict_params_s": 0.06746661900000106, + "metrics": { + "nll": 1.096392045323513, + "crps": 0.40381986836337114, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "3f816168712d3b935609d80392fe074faac068c4372a1ac25d2267594581301c", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.201210966000005, + "predict_params_s": 0.8796572620000092, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 3.2026938079999923, + "predict_params_s": 0.6871226760000013, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.509326623000007, + "predict_params_s": 0.016671253000012598, + "metrics": { + "nll": 1.096392041715409, + "crps": 0.403819866890205, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.201305627822876, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.1229389905929565, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "9af00eccc01727765f72dde3b81530643aa5427a31ade9b25451d0dcdf00f7cc" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23239498899999944, + "predict_params_s": 0.01806297500000653, + "metrics": { + "nll": 1.0963920449466849, + "crps": 0.4038198678278385, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "a5e059e04cdc02fa03b1f05dde2cd3b5264ca34bf3ca4f27fd6d86ae372fa712", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + } + ] + }, + "remote_function_wall_s": 108.876481186, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/README.md b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/README.md new file mode 100644 index 0000000..c027d2b --- /dev/null +++ b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/README.md @@ -0,0 +1,47 @@ +# P4.1 batch histogram: real T4 validation + +Clean source: `cf61611a5fc9c4d701de65aa97dae082fbc89003`. +Wheel SHA256: `19b63c3c0ea7da16bbf34334c04ca15217bab70ce6cf9fd646e87c2aad3d13a4`. + +Result: **3 passed / 0 skipped** (two existing smoke tests plus the batch +histogram device/oracle test). Test execution 19.47 s; remote function 23.49 s, +including checks/JIT, excluding image build/startup. These are validation +runtimes, not training performance measurements or billed durations. + +The histogram test checks a 6-row weighted/missing/constant-feature fixture, +a 4,097-row random fixture (seed 103), and an empty input. G/H are compared with +independent float64 direct sample sums and the CPU implementation. Counts match +exactly. Maximum absolute G error is 9.835e-7, H error 1.252e-6; small and empty +fixtures are exact. Inputs are hashed in `results.json` alongside dimensions, +slot counts and allocated bytes. The top-level manifest dataset describes the +existing smoke fixture; histogram fixture metadata lives in these case records. + +Outputs remain CuPy arrays, with non-default stream synchronization verified. +The test blocks `cupy.asnumpy`, Numba `copy_to_host` and the legacy dictionary +histogram wrapper during aggregation. This checks named boundaries only; +scalar validity checks intentionally synchronize. No profiler/PCIe trace or +whole-process zero-transfer claim. Invalid devices/H/IDs and insufficient +returned-buffer budgets are rejected. Atomic float summation is not bitwise +deterministic across runs. No downstream GPU split/tree/quality claim follows. + +Environment: Tesla T4 15 GiB, driver 580.95.05, CUDA runtime reported 12.9, Python +3.12.1, CuPy 13.6.0, NumPy 2.3.5, Numba 0.63.1 / numba-cuda 0.27.0. The pinned +base image identifies CUDA 12.4; the separately recorded runtime is authoritative +for the loaded Python stack. Requested CPU=2, RAM=8192 MiB, thread settings=2. +The host CPU model is unavailable (`unknown`); full environment is in results. + +Reproduce from the source commit with a clean checkout: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite histograms +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_histograms +``` + +The prepare step builds a fresh wheel and hashes the precise upload allowlist. +The runner requires all three cases, wheel/source identity, installed-file +verification, actual CUDA execution and the histogram device checks. Validate +this saved result offline with: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T150940Z-a5c80f7f +``` diff --git a/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/junit.xml b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/junit.xml new file mode 100644 index 0000000..68b0872 --- /dev/null +++ b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/manifest.json b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/manifest.json new file mode 100644 index 0000000..a1d91f6 --- /dev/null +++ b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/manifest.json @@ -0,0 +1,46 @@ +{ + "schema_version": 1, + "suite": "histograms", + "test_files": [ + "test_smoke.py", + "test_histograms.py" + ], + "source_sha": "cf61611a5fc9c4d701de65aa97dae082fbc89003", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "19b63c3c0ea7da16bbf34334c04ca15217bab70ce6cf9fd646e87c2aad3d13a4", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_histograms" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T150940Z-a5c80f7f", + "modal_image_id": "im-4IWykh1UK32hoTGCbOtKdg" +} diff --git a/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/results.json b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/results.json new file mode 100644 index 0000000..5715a92 --- /dev/null +++ b/benchmarks/results/foundation/20260905T150940Z-a5c80f7f/results.json @@ -0,0 +1,138 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 266818007040, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "cuda-core": "0.6.0", + "pluggy": "1.6.0", + "numpy": "2.3.5", + "execnet": "2.1.2", + "scipy": "1.16.3", + "packaging": "25.0", + "cuda-pathfinder": "1.4.0", + "pytest": "9.0.2", + "pip": "23.3.2", + "joblib": "1.5.3", + "openboost": "1.0.0rc1", + "cuda-bindings": "13.1.1", + "numba-cuda": "0.27.0", + "numba": "0.63.1", + "coverage": "7.13.1", + "cupy-cuda12x": "13.6.0", + "setuptools": "69.0.3", + "llvmlite": "0.46.0", + "pytest-cov": "7.0.0", + "Pygments": "2.19.2", + "pytest-xdist": "3.8.0", + "fastrlock": "0.8.3", + "iniconfig": "2.3.0", + "aiohttp": "3.12.7", + "hpack": "4.1.0", + "protobuf": "6.31.1", + "aiosignal": "1.3.2", + "h2": "4.2.0", + "grpclib": "0.4.8", + "hyperframe": "6.1.0", + "aiohappyeyeballs": "2.6.1", + "frozenlist": "1.6.0", + "cbor2": "5.7.0", + "idna": "3.10", + "propcache": "0.3.1", + "certifi": "2025.4.26", + "yarl": "1.20.0", + "typing_extensions": "4.13.2", + "multidict": "6.4.4", + "attrs": "25.3.0" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "cf61611a5fc9c4d701de65aa97dae082fbc89003", + "wheel_sha256": "19b63c3c0ea7da16bbf34334c04ca15217bab70ce6cf9fd646e87c2aad3d13a4", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n3 passed, 18 warnings in 19.47s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 42, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.2516975402832031e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + } + }, + "remote_function_wall_s": 23.490344378, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/README.md b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/README.md new file mode 100644 index 0000000..b039bb8 --- /dev/null +++ b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/README.md @@ -0,0 +1,52 @@ +# P4.2 numeric split/routing: real T4 validation + +Clean source: `b75b95a372ed87c36141cdf626115f74bc454989`. +Wheel SHA256: `7ab786cab2f23b462571725ea0f11cc73988f24d0eb01b11cf929a9eaa7f5ed6`. + +**4 passed / 0 skipped**: two existing smoke cases, histogram regression, and +split/routing oracle. Pytest 20.23 s; remote function 24.79 s, including checks +and JIT, excluding image/startup. These are verification durations, not a +training speed measurement or billed runtime. + +Split expected values come from exhaustive direct row masks and float64 sample +sums, independently of production histograms. Weighted and zero-weight samples, +L2/unregularized settings and excessive child/gain thresholds are covered. +Features, thresholds and routed IDs match exactly; gain tolerance is +rtol=atol=1e-10 on these exactly representable inputs. Child histograms are +rebuilt from routed rows and their G/H totals checked against the rows; next +level split topology is also independently verified. This is not parent +histogram scaling. The saved input hashes/parameters/topology/gains/IDs are in +`results.json`; the top-level manifest dataset describes the existing smoke +fixture, not these split cases. + +Separate exact tie and min_gain equality checks pass, including inclusive child +weight equality. Negative/zero/no-legal gains, constant features, inactive and +terminal slots, empty routing, invalid IDs/child indices and missing-bin +rejections pass. Arrays remain CuPy arrays; named full-array download wrappers +are blocked around histogram/split/routing. Scalar validation syncs are allowed; +there is no profiler trace or whole-process zero-transfer claim. The histogram +regression retains its non-default stream test; this is not a stream guarantee +for an assembled trainer. + +Boundary: numeric L2 only; positive curvature required in each child, including +when min_child_weight=0. Missing/categorical builder support and end-to-end +experimental GPU training remain unimplemented. No adoption or cost advantage +can be inferred from these primitive tests. + +Environment: Tesla T4 15 GiB, driver 580.95.05, reported CUDA runtime 12.9, +Python 3.12.1, CuPy 13.6.0, NumPy 2.3.5, Numba 0.63.1 / numba-cuda 0.27.0. +Requested CPU=2, RAM=8192 MiB, thread settings=2. CPU model is unknown. +Full package/image/environment provenance is retained in manifest/results. + +Reproduce from the clean source commit: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite splits +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_splits +``` + +Validate this saved evidence offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T151819Z-e5eb30b7 +``` diff --git a/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/junit.xml b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/junit.xml new file mode 100644 index 0000000..a4b1ea8 --- /dev/null +++ b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/manifest.json b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/manifest.json new file mode 100644 index 0000000..653d72a --- /dev/null +++ b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/manifest.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "suite": "splits", + "test_files": [ + "test_smoke.py", + "test_histograms.py", + "test_splits.py" + ], + "source_sha": "b75b95a372ed87c36141cdf626115f74bc454989", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "7ab786cab2f23b462571725ea0f11cc73988f24d0eb01b11cf929a9eaa7f5ed6", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340", + "test_splits.py": "8fa1f4403412a5bb7d2fc4602ca4470c0047f5f587fb517860411ca1326397b6", + "split_oracle.py": "fc95859d6c9306af81d77ea31c799ea6cbd29bfec38f478012dddc873e7b7635" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_splits" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T151819Z-e5eb30b7", + "modal_image_id": "im-W8UaYXmLnvis029ST6b1VW" +} diff --git a/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/results.json b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/results.json new file mode 100644 index 0000000..655ba98 --- /dev/null +++ b/benchmarks/results/foundation/20260905T151819Z-e5eb30b7/results.json @@ -0,0 +1,318 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784177152, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "execnet": "2.1.2", + "pip": "23.3.2", + "cuda-bindings": "13.1.1", + "cuda-pathfinder": "1.4.0", + "cupy-cuda12x": "13.6.0", + "Pygments": "2.19.2", + "scipy": "1.16.3", + "pytest-xdist": "3.8.0", + "numba-cuda": "0.27.0", + "numba": "0.63.1", + "cuda-core": "0.6.0", + "openboost": "1.0.0rc1", + "iniconfig": "2.3.0", + "numpy": "2.3.5", + "pytest-cov": "7.0.0", + "packaging": "25.0", + "setuptools": "69.0.3", + "joblib": "1.5.3", + "fastrlock": "0.8.3", + "llvmlite": "0.46.0", + "coverage": "7.13.1", + "pytest": "9.0.2", + "pluggy": "1.6.0", + "typing_extensions": "4.13.2", + "protobuf": "6.31.1", + "aiohttp": "3.12.7", + "grpclib": "0.4.8", + "aiohappyeyeballs": "2.6.1", + "cbor2": "5.7.0", + "aiosignal": "1.3.2", + "attrs": "25.3.0", + "certifi": "2025.4.26", + "propcache": "0.3.1", + "yarl": "1.20.0", + "idna": "3.10", + "h2": "4.2.0", + "hyperframe": "6.1.0", + "multidict": "6.4.4", + "frozenlist": "1.6.0", + "hpack": "4.1.0" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "b75b95a372ed87c36141cdf626115f74bc454989", + "wheel_sha256": "7ab786cab2f23b462571725ea0f11cc73988f24d0eb01b11cf929a9eaa7f5ed6", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": ".... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n4 passed, 18 warnings in 20.23s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 43, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.2516975402832031e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + }, + "batch_splits": { + "device_arrays": true, + "routed_child_oracle": true, + "cases": [ + { + "parameters": {}, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 65.05263157894737, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + -1 + ] + }, + { + "parameters": { + "reg_lambda": 0.0, + "min_child_weight": 0.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 2, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 83.16577540106952, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 1, + 1, + 2, + -1 + ] + }, + { + "parameters": { + "min_gain": 1000000.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + }, + { + "parameters": { + "min_child_weight": 20.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + } + ], + "exact_ties_and_gain_boundary": true, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim" + } + }, + "remote_function_wall_s": 24.786382396, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T152639Z-62d96727/README.md b/benchmarks/results/foundation/20260905T152639Z-62d96727/README.md new file mode 100644 index 0000000..863d9ad --- /dev/null +++ b/benchmarks/results/foundation/20260905T152639Z-62d96727/README.md @@ -0,0 +1,56 @@ +# P4.3 leaf reduction/rule: real T4 validation + +Clean source: `4da7f4bdef0d981a141a326ed00d9baa684056d8`. +Wheel SHA256: `0b6eeb2b8e047f8763ad3d4999adda50527af66c7bd9cf94dcce8c3103d43d4c`. + +**5 passed / 0 skipped**: existing two smoke cases, histogram, split/routing, +and leaf rule/reduction. Pytest 23.90 s; remote function 28.17 s, +including checks/JIT, excluding image/startup. These are validation durations, +not a training benchmark or billed runtime. + +Direct row sums independently verify weighted G/H and physical sample counts +for a 6-row fixture, a 4,097-row fixture (seed 109) and empty input. All sums +and counts match exactly for these dyadic inputs. Float32 Newton/clipped values +match the float64 mathematical reference at rtol=atol=1e-6. Input hashes, +dimensions, sums/counts and actual values are retained in results.json. The +top-level manifest dataset describes the pre-existing smoke fixture. + +Two GPU-composed rounds with y=[2,4], weights=[1,3], lambda=1 and coefficient=1 +produce raw≈3.36 with Newton leaves versus raw=1 with bound=0.5. The second +weighted gradient is approximately [0.8,-3.6] versus exactly [-1.5,-10.5]. +Thus the rule changes both leaf output and the next objective input. This GPU +check composes primitives; a separate local CPU test exercises the actual +experimental Booster via a custom root builder. Neither proves the still +unassembled experimental GPU Booster. + +The suite checks empty/zero curvature, nonzero-gradient/zero-denominator error, +wrong output device/dtype/finiteness, nonzero inactive values and GPU mutation +of rule inputs. Reduction/rule arrays remain CuPy arrays; non-default stream +execution passes. Named cupy.asnumpy, Numba copy_to_host and legacy leaf wrapper +calls are blocked during relevant operations. Scalar validation synchronization +is allowed; no profiler trace or whole-process zero-transfer claim. Float32 +atomic order is not generally deterministic beyond these exact fixtures. + +Environment: Tesla T4, Tesla T4, 580.95.05, 15360 MiB, CUDA runtime 12090, +CUDA driver API 13000, Python 3.12.1, +CuPy 13.6.0, NumPy 2.3.5, +Numba 0.63.1 / numba-cuda 0.27.0. +Requested CPU=2, RAM=8192 MiB, thread settings=2; CPU model=unknown. +Full package/image/environment provenance is in manifest/results. + +Reproduce from the clean source commit: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite leaves +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_leaves +``` + +Validate the saved artifact offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T152639Z-62d96727 +``` + +Boundary: default Newton rule supports L2, and clipping keeps the existing +split criterion. Builder assembly, independent installation/use and complete +GPU training/quality/cost remain later gates. No external adoption is claimed. diff --git a/benchmarks/results/foundation/20260905T152639Z-62d96727/junit.xml b/benchmarks/results/foundation/20260905T152639Z-62d96727/junit.xml new file mode 100644 index 0000000..033fade --- /dev/null +++ b/benchmarks/results/foundation/20260905T152639Z-62d96727/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T152639Z-62d96727/manifest.json b/benchmarks/results/foundation/20260905T152639Z-62d96727/manifest.json new file mode 100644 index 0000000..5287110 --- /dev/null +++ b/benchmarks/results/foundation/20260905T152639Z-62d96727/manifest.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "suite": "leaves", + "test_files": [ + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py" + ], + "source_sha": "4da7f4bdef0d981a141a326ed00d9baa684056d8", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "0b6eeb2b8e047f8763ad3d4999adda50527af66c7bd9cf94dcce8c3103d43d4c", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340", + "test_splits.py": "8fa1f4403412a5bb7d2fc4602ca4470c0047f5f587fb517860411ca1326397b6", + "split_oracle.py": "fc95859d6c9306af81d77ea31c799ea6cbd29bfec38f478012dddc873e7b7635", + "test_leaves.py": "c6b1a4827d5c3b381802f412438ddbea48e6eac996c0c511d2e9673230ba2d60", + "leaf_oracle.py": "8dc8c6132c3061691c95f1cbe424cbd5be9ba11273f2e37432b6c20855e83018" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_leaves" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T152639Z-62d96727", + "modal_image_id": "im-ZOjWO7K4mxeAllXddOGxbG" +} diff --git a/benchmarks/results/foundation/20260905T152639Z-62d96727/results.json b/benchmarks/results/foundation/20260905T152639Z-62d96727/results.json new file mode 100644 index 0000000..bf9abd1 --- /dev/null +++ b/benchmarks/results/foundation/20260905T152639Z-62d96727/results.json @@ -0,0 +1,467 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 266817941504, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "cuda-pathfinder": "1.4.0", + "pluggy": "1.6.0", + "cuda-bindings": "13.1.1", + "Pygments": "2.19.2", + "llvmlite": "0.46.0", + "openboost": "1.0.0rc1", + "numba-cuda": "0.27.0", + "cuda-core": "0.6.0", + "pytest-cov": "7.0.0", + "pip": "23.3.2", + "execnet": "2.1.2", + "coverage": "7.13.1", + "cupy-cuda12x": "13.6.0", + "setuptools": "69.0.3", + "pytest-xdist": "3.8.0", + "numba": "0.63.1", + "packaging": "25.0", + "pytest": "9.0.2", + "fastrlock": "0.8.3", + "iniconfig": "2.3.0", + "numpy": "2.3.5", + "joblib": "1.5.3", + "scipy": "1.16.3", + "protobuf": "6.31.1", + "cbor2": "5.7.0", + "hpack": "4.1.0", + "certifi": "2025.4.26", + "hyperframe": "6.1.0", + "typing_extensions": "4.13.2", + "aiosignal": "1.3.2", + "yarl": "1.20.0", + "frozenlist": "1.6.0", + "aiohttp": "3.12.7", + "aiohappyeyeballs": "2.6.1", + "idna": "3.10", + "multidict": "6.4.4", + "grpclib": "0.4.8", + "h2": "4.2.0", + "attrs": "25.3.0", + "propcache": "0.3.1" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "4da7f4bdef0d981a141a326ed00d9baa684056d8", + "wheel_sha256": "0b6eeb2b8e047f8763ad3d4999adda50527af66c7bd9cf94dcce8c3103d43d4c", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "..... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n5 passed, 18 warnings in 23.90s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 44, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.2516975402832031e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + }, + "batch_splits": { + "device_arrays": true, + "routed_child_oracle": true, + "cases": [ + { + "parameters": {}, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 65.05263157894737, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + -1 + ] + }, + { + "parameters": { + "reg_lambda": 0.0, + "min_child_weight": 0.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 2, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 83.16577540106952, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 1, + 1, + 2, + -1 + ] + }, + { + "parameters": { + "min_gain": 1000000.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + }, + { + "parameters": { + "min_child_weight": 20.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + } + ], + "exact_ties_and_gain_boundary": true, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim" + }, + "batch_leaves": { + "device_arrays": true, + "row_sum_oracle": true, + "bounded_changes_next_gradient": true, + "cases": [ + { + "input_sha256": "6798811eba51f86e0857c90969b3d6628bd88328a97dc9a5c1c40a879a65a8da", + "samples": 6, + "slots": 4, + "seed": null, + "counts": [ + 3, + 0, + 1, + 0 + ], + "grad": [ + -14.0, + 0.0, + -3.0, + 0.0 + ], + "hess": [ + 4.0, + 0.0, + 1.0, + 0.0 + ], + "values": [ + 2.799999952316284, + -0.0, + 1.5, + -0.0 + ], + "bounded": [ + 0.5, + -0.0, + 0.5, + -0.0 + ] + }, + { + "input_sha256": "4dc60aca864b164786f1008cdb7789c82778b78df569e1a92207c5209b3a9ccf", + "samples": 4097, + "slots": 7, + "seed": 109, + "counts": [ + 519, + 0, + 512, + 475, + 0, + 531, + 539 + ], + "grad": [ + -2.5, + 0.0, + 176.0, + 228.0, + 0.0, + 120.0, + 4.5 + ], + "hess": [ + 612.0, + 0.0, + 535.5, + 521.5, + 0.0, + 607.5, + 623.5 + ], + "values": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ], + "bounded": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ] + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "samples": 0, + "slots": 3, + "seed": null, + "counts": [ + 0, + 0, + 0 + ], + "grad": [ + 0.0, + 0.0, + 0.0 + ], + "hess": [ + 0.0, + 0.0, + 0.0 + ], + "values": [ + -0.0, + -0.0, + -0.0 + ], + "bounded": [ + -0.0, + -0.0, + -0.0 + ] + } + ], + "two_rounds": [ + { + "raw": [ + 3.3600001335144043, + 3.3600001335144043 + ], + "second_gradient": [ + 0.7999999523162842, + -3.6000001430511475 + ] + }, + { + "raw": [ + 1.0, + 1.0 + ], + "second_gradient": [ + -1.5, + -10.5 + ] + } + ], + "scope": "GPU primitive composition; CPU trainer tested separately; not assembled GPU Booster" + } + }, + "remote_function_wall_s": 28.171784470000002, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T163823Z-7b16b556/README.md b/benchmarks/results/foundation/20260905T163823Z-7b16b556/README.md new file mode 100644 index 0000000..1bfe639 --- /dev/null +++ b/benchmarks/results/foundation/20260905T163823Z-7b16b556/README.md @@ -0,0 +1,52 @@ +# P4.4 level-wise builder: real T4 validation + +Clean source: `e02403bb0e571436e7a631f8bd5c562cac911424`. +Wheel SHA256: `46dec4c697a5dfa21d8c6bca7e17a26019fd3c3373f0dd2ad539eed3ff819450`. + +**6 passed / 0 skipped**: two smoke cases, histogram, split/routing, leaf +reduction/rule and whole-tree builder. Pytest 25.22 s; remote function 29.88 s. +These are validation durations including JIT, excluding image/startup, not +training performance or billed-runtime claims. + +The small whole-tree test compares all compact tree arrays and sample predictions +against an independent recursive row-mask oracle. Device views are released and +the memory pool cleared before cached prediction is checked. Default-stream, +numeric/nonmissing, L2/full-sampling and histogram-budget boundaries are tested. + +Four synthetic cells use 16/4097 rows, seed 127, two Normal parameters and two +rounds with nonconstant channel coefficients, with/without bounded leaves. +Maximum CPU/CUDA raw error is 1.7881393432617188e-7; NLL and CRPS agree within +2e-5 absolute/relative tolerance. Clipping changes final predictions. Each cell +saves standard trees and reproduces raw prediction after CPU load. Exact data +hashes and individual metrics are in results.json; top-level manifest data +identifies the inherited smoke fixture. + +During 17 GPU builds, the named copy spy observes exactly five compact arrays +per tree: 85 calls, 2,380 bytes total. Sample-sized and histogram-shaped arrays +are rejected by that spy and Numba copy_to_host is blocked. Scalar validation +synchronization is allowed. This is not a profiler/whole-process transfer audit. + +Scope: direct GPU builder composition; experimental GPU Booster.fit remains P5. +CPU Booster can opt into this builder; its broader existing default is retained. +These training-fixture metrics establish numerical agreement, not held-out +quality, calibration, speed, cost savings or external adoption. + +Environment: Tesla T4, driver 580.95.05, 15360 MiB, CUDA runtime 12090, +CUDA driver API 13000, Python 3.12.1, CuPy 13.6.0, NumPy 2.3.5, +Numba 0.63.1 / numba-cuda 0.27.0. Requested CPU=2, RAM=8192 MiB, +thread settings=2; CPU model unknown. Complete environment/image/package +provenance is retained in manifest/results. All 12 uploaded source hashes and +uv.lock match the source commit; embedded and separate JUnit reports match. + +Reproduce from the clean source commit: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite builder +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_builder +``` + +Validate saved evidence offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T163823Z-7b16b556 +``` diff --git a/benchmarks/results/foundation/20260905T163823Z-7b16b556/junit.xml b/benchmarks/results/foundation/20260905T163823Z-7b16b556/junit.xml new file mode 100644 index 0000000..a8c4cab --- /dev/null +++ b/benchmarks/results/foundation/20260905T163823Z-7b16b556/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T163823Z-7b16b556/manifest.json b/benchmarks/results/foundation/20260905T163823Z-7b16b556/manifest.json new file mode 100644 index 0000000..90c8eae --- /dev/null +++ b/benchmarks/results/foundation/20260905T163823Z-7b16b556/manifest.json @@ -0,0 +1,55 @@ +{ + "schema_version": 1, + "suite": "builder", + "test_files": [ + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py" + ], + "source_sha": "e02403bb0e571436e7a631f8bd5c562cac911424", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "46dec4c697a5dfa21d8c6bca7e17a26019fd3c3373f0dd2ad539eed3ff819450", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340", + "test_splits.py": "8fa1f4403412a5bb7d2fc4602ca4470c0047f5f587fb517860411ca1326397b6", + "split_oracle.py": "fc95859d6c9306af81d77ea31c799ea6cbd29bfec38f478012dddc873e7b7635", + "test_leaves.py": "c6b1a4827d5c3b381802f412438ddbea48e6eac996c0c511d2e9673230ba2d60", + "leaf_oracle.py": "8dc8c6132c3061691c95f1cbe424cbd5be9ba11273f2e37432b6c20855e83018", + "test_builder.py": "f7b9256c028c5e86471c5be21cc549f020ef42873e3cb26563c2cbe75d53299c", + "builder_oracle.py": "1b23491bf7c20159b941f38b586f00f597d2355f63026e373c7efc1159d554a6" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_builder" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T163823Z-7b16b556", + "modal_image_id": "im-6p2P9WdAaQxhSWQpq70vOh" +} diff --git a/benchmarks/results/foundation/20260905T163823Z-7b16b556/results.json b/benchmarks/results/foundation/20260905T163823Z-7b16b556/results.json new file mode 100644 index 0000000..fc13673 --- /dev/null +++ b/benchmarks/results/foundation/20260905T163823Z-7b16b556/results.json @@ -0,0 +1,537 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784197632, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "cuda-pathfinder": "1.4.0", + "cuda-core": "0.6.0", + "cupy-cuda12x": "13.6.0", + "numba": "0.63.1", + "fastrlock": "0.8.3", + "scipy": "1.16.3", + "pytest-cov": "7.0.0", + "pytest": "9.0.2", + "coverage": "7.13.1", + "joblib": "1.5.3", + "setuptools": "69.0.3", + "llvmlite": "0.46.0", + "numpy": "2.3.5", + "cuda-bindings": "13.1.1", + "numba-cuda": "0.27.0", + "pluggy": "1.6.0", + "openboost": "1.0.0rc1", + "Pygments": "2.19.2", + "pip": "23.3.2", + "execnet": "2.1.2", + "pytest-xdist": "3.8.0", + "iniconfig": "2.3.0", + "packaging": "25.0", + "attrs": "25.3.0", + "frozenlist": "1.6.0", + "idna": "3.10", + "grpclib": "0.4.8", + "protobuf": "6.31.1", + "hyperframe": "6.1.0", + "cbor2": "5.7.0", + "aiosignal": "1.3.2", + "multidict": "6.4.4", + "hpack": "4.1.0", + "h2": "4.2.0", + "aiohappyeyeballs": "2.6.1", + "propcache": "0.3.1", + "yarl": "1.20.0", + "aiohttp": "3.12.7", + "typing_extensions": "4.13.2", + "certifi": "2025.4.26" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "e02403bb0e571436e7a631f8bd5c562cac911424", + "wheel_sha256": "46dec4c697a5dfa21d8c6bca7e17a26019fd3c3373f0dd2ad539eed3ff819450", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "...... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n6 passed, 18 warnings in 25.22s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 45, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.2516975402832031e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + }, + "batch_splits": { + "device_arrays": true, + "routed_child_oracle": true, + "cases": [ + { + "parameters": {}, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 65.05263157894737, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + -1 + ] + }, + { + "parameters": { + "reg_lambda": 0.0, + "min_child_weight": 0.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 2, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 83.16577540106952, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 1, + 1, + 2, + -1 + ] + }, + { + "parameters": { + "min_gain": 1000000.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + }, + { + "parameters": { + "min_child_weight": 20.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + } + ], + "exact_ties_and_gain_boundary": true, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim" + }, + "batch_leaves": { + "device_arrays": true, + "row_sum_oracle": true, + "bounded_changes_next_gradient": true, + "cases": [ + { + "input_sha256": "6798811eba51f86e0857c90969b3d6628bd88328a97dc9a5c1c40a879a65a8da", + "samples": 6, + "slots": 4, + "seed": null, + "counts": [ + 3, + 0, + 1, + 0 + ], + "grad": [ + -14.0, + 0.0, + -3.0, + 0.0 + ], + "hess": [ + 4.0, + 0.0, + 1.0, + 0.0 + ], + "values": [ + 2.799999952316284, + -0.0, + 1.5, + -0.0 + ], + "bounded": [ + 0.5, + -0.0, + 0.5, + -0.0 + ] + }, + { + "input_sha256": "4dc60aca864b164786f1008cdb7789c82778b78df569e1a92207c5209b3a9ccf", + "samples": 4097, + "slots": 7, + "seed": 109, + "counts": [ + 519, + 0, + 512, + 475, + 0, + 531, + 539 + ], + "grad": [ + -2.5, + 0.0, + 176.0, + 228.0, + 0.0, + 120.0, + 4.5 + ], + "hess": [ + 612.0, + 0.0, + 535.5, + 521.5, + 0.0, + 607.5, + 623.5 + ], + "values": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ], + "bounded": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ] + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "samples": 0, + "slots": 3, + "seed": null, + "counts": [ + 0, + 0, + 0 + ], + "grad": [ + 0.0, + 0.0, + 0.0 + ], + "hess": [ + 0.0, + 0.0, + 0.0 + ], + "values": [ + -0.0, + -0.0, + -0.0 + ], + "bounded": [ + -0.0, + -0.0, + -0.0 + ] + } + ], + "two_rounds": [ + { + "raw": [ + 3.3600001335144043, + 3.3600001335144043 + ], + "second_gradient": [ + 0.7999999523162842, + -3.6000001430511475 + ] + }, + { + "raw": [ + 1.0, + 1.0 + ], + "second_gradient": [ + -1.5, + -10.5 + ] + } + ], + "scope": "GPU primitive composition; CPU trainer tested separately; not assembled GPU Booster" + }, + "levelwise_builder": { + "device_cache_survives_owner_release": true, + "compact_transfer_calls": 85, + "compact_transfer_bytes": 2380, + "two_channel_cases": [ + { + "samples": 16, + "seed": 127, + "clipped": false, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.376172378638702, + "crps": 0.5607174393426145 + }, + "cuda": { + "nll": 1.3761723780246782, + "crps": 0.5607174390134131 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 16, + "seed": 127, + "clipped": true, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.4545325936069222, + "crps": 0.6108751652432844 + }, + "cuda": { + "nll": 1.4545325948769345, + "crps": 0.6108751656755558 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 4097, + "seed": 127, + "clipped": false, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.339077446639963, + "crps": 0.5416246632667225 + }, + "cuda": { + "nll": 1.339077424477956, + "crps": 0.5416246466358043 + }, + "max_raw_error": 1.7881393432617188e-07 + }, + { + "samples": 4097, + "seed": 127, + "clipped": true, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.5450022972468154, + "crps": 0.6447824943510968 + }, + "cuda": { + "nll": 1.5450022978344107, + "crps": 0.6447824954205831 + }, + "max_raw_error": 2.2351741790771484e-08 + } + ], + "cpu_load_prediction": true, + "scope": "direct GPU builder composition; GPU Booster.fit remains P5; named transfers only" + } + }, + "remote_function_wall_s": 29.877819805999998, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T175651Z-b1f9743a/README.md b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/README.md new file mode 100644 index 0000000..aa4a90f --- /dev/null +++ b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/README.md @@ -0,0 +1,33 @@ +# P5.1 initial strict CUDA trainer evidence + +Clean source `8c34b266958a4975e3d6770f407e019a4f20ef87`, wheel SHA256 +`5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed`. + +7 passed / 0 skipped on real T4. Actual experimental Booster.fit runs two rounds, +two parameters and nonuniform/zero weights with nonconstant coefficients. +Default/explicit LevelWiseBuilder fits match CPU raw predictions within maximum +1.1920928955078125e-7, NLL exactly for this fixture, and CRPS within 3e-9. +CPU load predictions are exact; runtime failure, wrong device output, input +mutation and invalid cached predictions exercise rollback. Legacy native dispatch +and named sample-download wrappers are blocked during measured strict fits. +40 compact array downloads / 1,120 bytes across those eight trees are recorded; +this does not audit all transfer APIs or scalar synchronization. The session also +performs device defensive input copies and compact tree verification uploads. + +Pytest 38.48 s, remote function 42.96 s (validation/JIT duration, not benchmark +or billed runtime). Full environment, input hash, source and package provenance +are in manifest/results. nsys is unavailable; no profiler trace was collected. +Source hashes match the clean git revision and separate/embedded JUnit match. + +This initial slice verifies Normal natural-gradient shared trainer execution. +A following test slice expands adapter-mode and external-builder coverage. +To reproduce this exact initial suite use the clean source revision: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite trainer +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_trainer +``` + +Later runner versions require the expanded evidence. For offline validation of +this initial artifact, use `python -m benchmarks.foundation.runner` from its +source revision. No GPU speed, held-out quality or external adoption is claimed. diff --git a/benchmarks/results/foundation/20260905T175651Z-b1f9743a/junit.xml b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/junit.xml new file mode 100644 index 0000000..0ca7481 --- /dev/null +++ b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T175651Z-b1f9743a/manifest.json b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/manifest.json new file mode 100644 index 0000000..f3e0c2a --- /dev/null +++ b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/manifest.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "suite": "trainer", + "test_files": [ + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "test_trainer.py" + ], + "source_sha": "8c34b266958a4975e3d6770f407e019a4f20ef87", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340", + "test_splits.py": "8fa1f4403412a5bb7d2fc4602ca4470c0047f5f587fb517860411ca1326397b6", + "split_oracle.py": "fc95859d6c9306af81d77ea31c799ea6cbd29bfec38f478012dddc873e7b7635", + "test_leaves.py": "c6b1a4827d5c3b381802f412438ddbea48e6eac996c0c511d2e9673230ba2d60", + "leaf_oracle.py": "8dc8c6132c3061691c95f1cbe424cbd5be9ba11273f2e37432b6c20855e83018", + "test_builder.py": "f7b9256c028c5e86471c5be21cc549f020ef42873e3cb26563c2cbe75d53299c", + "builder_oracle.py": "1b23491bf7c20159b941f38b586f00f597d2355f63026e373c7efc1159d554a6", + "test_trainer.py": "cba1fa2f67fcb17b462d4f20f819d682d3a82e233a4bcaba326f4c550d18043f" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_trainer" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T175651Z-b1f9743a", + "modal_image_id": "im-jEF68EDupyvZ57JEj1vsE4" +} diff --git a/benchmarks/results/foundation/20260905T175651Z-b1f9743a/results.json b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/results.json new file mode 100644 index 0000000..61b8e3d --- /dev/null +++ b/benchmarks/results/foundation/20260905T175651Z-b1f9743a/results.json @@ -0,0 +1,608 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784218112, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "numba": "0.63.1", + "numpy": "2.3.5", + "cuda-pathfinder": "1.4.0", + "setuptools": "69.0.3", + "iniconfig": "2.3.0", + "joblib": "1.5.3", + "pluggy": "1.6.0", + "cuda-core": "0.6.0", + "fastrlock": "0.8.3", + "Pygments": "2.19.2", + "cupy-cuda12x": "13.6.0", + "execnet": "2.1.2", + "cuda-bindings": "13.1.1", + "openboost": "1.0.0rc1", + "scipy": "1.16.3", + "llvmlite": "0.46.0", + "coverage": "7.13.1", + "pytest-cov": "7.0.0", + "packaging": "25.0", + "pip": "23.3.2", + "numba-cuda": "0.27.0", + "pytest-xdist": "3.8.0", + "pytest": "9.0.2", + "propcache": "0.3.1", + "frozenlist": "1.6.0", + "multidict": "6.4.4", + "hyperframe": "6.1.0", + "protobuf": "6.31.1", + "attrs": "25.3.0", + "cbor2": "5.7.0", + "grpclib": "0.4.8", + "aiohttp": "3.12.7", + "certifi": "2025.4.26", + "yarl": "1.20.0", + "hpack": "4.1.0", + "aiohappyeyeballs": "2.6.1", + "h2": "4.2.0", + "aiosignal": "1.3.2", + "typing_extensions": "4.13.2", + "idna": "3.10" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "8c34b266958a4975e3d6770f407e019a4f20ef87", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "test_trainer.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "....... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n7 passed, 20 warnings in 38.48s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.4007091522216797e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + }, + "batch_splits": { + "device_arrays": true, + "routed_child_oracle": true, + "cases": [ + { + "parameters": {}, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 65.05263157894737, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + -1 + ] + }, + { + "parameters": { + "reg_lambda": 0.0, + "min_child_weight": 0.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 2, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 83.16577540106952, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 1, + 1, + 2, + -1 + ] + }, + { + "parameters": { + "min_gain": 1000000.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + }, + { + "parameters": { + "min_child_weight": 20.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + } + ], + "exact_ties_and_gain_boundary": true, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim" + }, + "batch_leaves": { + "device_arrays": true, + "row_sum_oracle": true, + "bounded_changes_next_gradient": true, + "cases": [ + { + "input_sha256": "6798811eba51f86e0857c90969b3d6628bd88328a97dc9a5c1c40a879a65a8da", + "samples": 6, + "slots": 4, + "seed": null, + "counts": [ + 3, + 0, + 1, + 0 + ], + "grad": [ + -14.0, + 0.0, + -3.0, + 0.0 + ], + "hess": [ + 4.0, + 0.0, + 1.0, + 0.0 + ], + "values": [ + 2.799999952316284, + -0.0, + 1.5, + -0.0 + ], + "bounded": [ + 0.5, + -0.0, + 0.5, + -0.0 + ] + }, + { + "input_sha256": "4dc60aca864b164786f1008cdb7789c82778b78df569e1a92207c5209b3a9ccf", + "samples": 4097, + "slots": 7, + "seed": 109, + "counts": [ + 519, + 0, + 512, + 475, + 0, + 531, + 539 + ], + "grad": [ + -2.5, + 0.0, + 176.0, + 228.0, + 0.0, + 120.0, + 4.5 + ], + "hess": [ + 612.0, + 0.0, + 535.5, + 521.5, + 0.0, + 607.5, + 623.5 + ], + "values": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ], + "bounded": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ] + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "samples": 0, + "slots": 3, + "seed": null, + "counts": [ + 0, + 0, + 0 + ], + "grad": [ + 0.0, + 0.0, + 0.0 + ], + "hess": [ + 0.0, + 0.0, + 0.0 + ], + "values": [ + -0.0, + -0.0, + -0.0 + ], + "bounded": [ + -0.0, + -0.0, + -0.0 + ] + } + ], + "two_rounds": [ + { + "raw": [ + 3.3600001335144043, + 3.3600001335144043 + ], + "second_gradient": [ + 0.7999999523162842, + -3.6000001430511475 + ] + }, + { + "raw": [ + 1.0, + 1.0 + ], + "second_gradient": [ + -1.5, + -10.5 + ] + } + ], + "scope": "GPU primitive composition; CPU trainer tested separately; not assembled GPU Booster" + }, + "levelwise_builder": { + "device_cache_survives_owner_release": true, + "compact_transfer_calls": 85, + "compact_transfer_bytes": 2380, + "two_channel_cases": [ + { + "samples": 16, + "seed": 127, + "clipped": false, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.376172378638702, + "crps": 0.5607174393426145 + }, + "cuda": { + "nll": 1.3761723780246782, + "crps": 0.5607174390134131 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 16, + "seed": 127, + "clipped": true, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.4545325936069222, + "crps": 0.6108751652432844 + }, + "cuda": { + "nll": 1.4545325948769345, + "crps": 0.6108751656755558 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 4097, + "seed": 127, + "clipped": false, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.339077446639963, + "crps": 0.5416246632667225 + }, + "cuda": { + "nll": 1.339077425710116, + "crps": 0.5416246499046051 + }, + "max_raw_error": 2.086162567138672e-07 + }, + { + "samples": 4097, + "seed": 127, + "clipped": true, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.5450022972468154, + "crps": 0.6447824943510968 + }, + "cuda": { + "nll": 1.5450022971267232, + "crps": 0.6447824952984369 + }, + "max_raw_error": 1.4901161193847656e-08 + } + ], + "cpu_load_prediction": true, + "scope": "direct GPU builder composition; GPU Booster.fit remains P5; named transfers only" + }, + "strict_extension_trainer": { + "actual_fit": true, + "legacy_dispatch_blocked": true, + "rollback": true, + "cpu_load_prediction": true, + "cases": [ + { + "explicit_builder": false, + "fit_report": { + "requested_device": "cuda", + "actual_device": "cuda", + "binning_device": "cpu", + "initialization_device": "cpu", + "objective_device": "cuda", + "tree_device": "cuda", + "update_device": "cuda", + "eval_device": null, + "builder_path": "LevelWiseBuilder", + "fallback_reason": null, + "random_state": 7, + "tree_counts": { + "loc": 2, + "scale": 2 + }, + "timing_scope": "No performance timing collected", + "transfer_scope": "one-time binned/y/weights upload; compact tree finalization downloads; device-only defensive input copies and cache verification; scalar validation syncs; predict_raw uses CPU trees; no profiler proof" + }, + "max_raw_error": 5.960464477539063e-08, + "cpu_crps": 0.5792920059096566, + "cuda_crps": 0.57929200316153, + "cpu_nll": 1.462673544883728, + "cuda_nll": 1.462673544883728 + }, + { + "explicit_builder": true, + "fit_report": { + "requested_device": "cuda", + "actual_device": "cuda", + "binning_device": "cpu", + "initialization_device": "cpu", + "objective_device": "cuda", + "tree_device": "cuda", + "update_device": "cuda", + "eval_device": null, + "builder_path": "LevelWiseBuilder", + "fallback_reason": null, + "random_state": 7, + "tree_counts": { + "loc": 2, + "scale": 2 + }, + "timing_scope": "No performance timing collected", + "transfer_scope": "one-time binned/y/weights upload; compact tree finalization downloads; device-only defensive input copies and cache verification; scalar validation syncs; predict_raw uses CPU trees; no profiler proof" + }, + "max_raw_error": 1.1920928955078125e-07, + "cpu_crps": 0.5792920059096566, + "cuda_crps": 0.5792920088473567, + "cpu_nll": 1.462673544883728, + "cuda_nll": 1.462673544883728 + } + ], + "compact_transfer_calls": 40, + "compact_transfer_bytes": 1120, + "data_sha256": "2b189618a4b1b0665498afa0ecd6abbe11e1d0231706995d747b65ad783ced6e", + "samples": 257, + "seed": 137, + "nsys_available": false, + "profiler_trace_collected": false, + "scope": "named transfer wrappers only; scalar syncs and device defensive copies allowed" + } + }, + "remote_function_wall_s": 42.955281561, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T180000Z-7d73ba83/README.md b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/README.md new file mode 100644 index 0000000..4cd5215 --- /dev/null +++ b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/README.md @@ -0,0 +1,64 @@ +# P5 strict CUDA trainer: complete declared adapter coverage + +Clean test source: `42998587ff9e2461d8f1935643f18d8dc20864cb`. +Implementation: `8c34b26` (unchanged by the test coverage commit). +Wheel SHA256: `5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed`. + +**7 passed / 0 skipped** on real T4: existing smoke and P4 primitives/builder, +plus actual strict experimental Booster.fit. Pytest 44.20 s, remote function +49.67 s. These include validation/JIT work, exclude image/startup, and are not +training benchmark or billed-runtime claims. + +The actual trainer runs CPU initialization/binning, then CuPy objective inputs, +statistics and raw updates. Two rounds, two parameters, nonuniform/zero weights +and a nonconstant schedule exercise both the default builder and a duck-typed +external builder delegating through the public LevelWiseBuilder API. The reports +identify LevelWiseBuilder versus ExternalBuilder; legacy native dispatch and +named sample-download wrappers are blocked during those fits. + +Maximum raw CPU/CUDA error across both scheduled fits and four adapter cells is +1.1920928955078125e-7. All Normal/Poisson × ordinary/natural gradient modes pass +at rtol=atol=2e-5. The scheduled Normal fixture matches weighted NLL exactly and +weighted CRPS within 3e-9. Exact target/data hashes, each error and metric are in +results.json. These are training-fixture numerical checks, not held-out quality. + +Saved GPU-trained models predict exactly after CPU loading. Broken kernels, +wrong-device outputs and input mutation preserve the previous fitted state; +inconsistent cached predictions fail and preserve predictions. Additional +negative cases reject float64 statistics, negative Hessians and aliased outputs. +CPU preflight checks cover unsupported features/parameters/evaluation and full +CPU fallback before device execution. + +The named spy records 40 compact downloads / 1,120 bytes for the two scheduled +fits (eight trees). This count excludes the additional adapter/error fixtures. +The session deliberately makes device copies of plugin inputs, including binned +values per tree, and uploads compact finalized trees for independent cache +verification. Scalar synchronization is allowed. **nsys is unavailable; no +profiler trace was collected.** This is not a whole-process zero-transfer or +performance claim. Optimization and matched-quality cost remain later work. + +Environment: Tesla T4, nvidia-smi `Tesla T4, 580.95.05, 15360 MiB`, +CUDA runtime 12090, driver API 13000, Python 3.12.1, +CuPy 13.6.0, NumPy 2.3.5, +Numba 0.63.1 / numba-cuda 0.27.0. +Requested CPU=2, RAM=8192 MiB, thread settings=2; CPU model unknown. Full image, +package and environment provenance is retained in manifest/results. + +Reproduce from the clean test source: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite trainer +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_trainer +``` + +Validate saved evidence offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T180000Z-7d73ba83 +``` + +All uploaded source hashes and lock hash matched git objects; embedded and separate +JUnit matched; private URL/local-path scans passed. Initial narrower evidence is +preserved alongside this artifact. Next: real installed P6 GPU extension packages. +Strict CUDA eval/callbacks/early stopping remain unsupported; no external adoption +or speed advantage is claimed. diff --git a/benchmarks/results/foundation/20260905T180000Z-7d73ba83/junit.xml b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/junit.xml new file mode 100644 index 0000000..b22ad38 --- /dev/null +++ b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T180000Z-7d73ba83/manifest.json b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/manifest.json new file mode 100644 index 0000000..88e74ee --- /dev/null +++ b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/manifest.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "suite": "trainer", + "test_files": [ + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "test_trainer.py" + ], + "source_sha": "42998587ff9e2461d8f1935643f18d8dc20864cb", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340", + "test_splits.py": "8fa1f4403412a5bb7d2fc4602ca4470c0047f5f587fb517860411ca1326397b6", + "split_oracle.py": "fc95859d6c9306af81d77ea31c799ea6cbd29bfec38f478012dddc873e7b7635", + "test_leaves.py": "c6b1a4827d5c3b381802f412438ddbea48e6eac996c0c511d2e9673230ba2d60", + "leaf_oracle.py": "8dc8c6132c3061691c95f1cbe424cbd5be9ba11273f2e37432b6c20855e83018", + "test_builder.py": "f7b9256c028c5e86471c5be21cc549f020ef42873e3cb26563c2cbe75d53299c", + "builder_oracle.py": "1b23491bf7c20159b941f38b586f00f597d2355f63026e373c7efc1159d554a6", + "test_trainer.py": "19aea65d712e4b6c23a9704f2ad1fca66ae6cb4163a5c315d084632fe600ec2b" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_trainer" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T180000Z-7d73ba83", + "modal_image_id": "im-jWJaNfNsoxbm6WsrjhPbKc" +} diff --git a/benchmarks/results/foundation/20260905T180000Z-7d73ba83/results.json b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/results.json new file mode 100644 index 0000000..210c4b9 --- /dev/null +++ b/benchmarks/results/foundation/20260905T180000Z-7d73ba83/results.json @@ -0,0 +1,635 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784234496, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "setuptools": "69.0.3", + "numba": "0.63.1", + "packaging": "25.0", + "pytest-xdist": "3.8.0", + "cuda-pathfinder": "1.4.0", + "pytest": "9.0.2", + "coverage": "7.13.1", + "pluggy": "1.6.0", + "cuda-core": "0.6.0", + "numpy": "2.3.5", + "fastrlock": "0.8.3", + "pytest-cov": "7.0.0", + "scipy": "1.16.3", + "numba-cuda": "0.27.0", + "Pygments": "2.19.2", + "llvmlite": "0.46.0", + "joblib": "1.5.3", + "cupy-cuda12x": "13.6.0", + "iniconfig": "2.3.0", + "pip": "23.3.2", + "openboost": "1.0.0rc1", + "cuda-bindings": "13.1.1", + "execnet": "2.1.2", + "certifi": "2025.4.26", + "propcache": "0.3.1", + "typing_extensions": "4.13.2", + "yarl": "1.20.0", + "multidict": "6.4.4", + "attrs": "25.3.0", + "cbor2": "5.7.0", + "grpclib": "0.4.8", + "aiohappyeyeballs": "2.6.1", + "hyperframe": "6.1.0", + "protobuf": "6.31.1", + "frozenlist": "1.6.0", + "h2": "4.2.0", + "aiosignal": "1.3.2", + "idna": "3.10", + "hpack": "4.1.0", + "aiohttp": "3.12.7" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "42998587ff9e2461d8f1935643f18d8dc20864cb", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "test_trainer.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "....... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n7 passed, 22 warnings in 44.20s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.4007091522216797e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + }, + "batch_splits": { + "device_arrays": true, + "routed_child_oracle": true, + "cases": [ + { + "parameters": {}, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 65.05263157894737, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + -1 + ] + }, + { + "parameters": { + "reg_lambda": 0.0, + "min_child_weight": 0.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 2, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 83.16577540106952, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 1, + 1, + 2, + -1 + ] + }, + { + "parameters": { + "min_gain": 1000000.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + }, + { + "parameters": { + "min_child_weight": 20.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + } + ], + "exact_ties_and_gain_boundary": true, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim" + }, + "batch_leaves": { + "device_arrays": true, + "row_sum_oracle": true, + "bounded_changes_next_gradient": true, + "cases": [ + { + "input_sha256": "6798811eba51f86e0857c90969b3d6628bd88328a97dc9a5c1c40a879a65a8da", + "samples": 6, + "slots": 4, + "seed": null, + "counts": [ + 3, + 0, + 1, + 0 + ], + "grad": [ + -14.0, + 0.0, + -3.0, + 0.0 + ], + "hess": [ + 4.0, + 0.0, + 1.0, + 0.0 + ], + "values": [ + 2.799999952316284, + -0.0, + 1.5, + -0.0 + ], + "bounded": [ + 0.5, + -0.0, + 0.5, + -0.0 + ] + }, + { + "input_sha256": "4dc60aca864b164786f1008cdb7789c82778b78df569e1a92207c5209b3a9ccf", + "samples": 4097, + "slots": 7, + "seed": 109, + "counts": [ + 519, + 0, + 512, + 475, + 0, + 531, + 539 + ], + "grad": [ + -2.5, + 0.0, + 176.0, + 228.0, + 0.0, + 120.0, + 4.5 + ], + "hess": [ + 612.0, + 0.0, + 535.5, + 521.5, + 0.0, + 607.5, + 623.5 + ], + "values": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ], + "bounded": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ] + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "samples": 0, + "slots": 3, + "seed": null, + "counts": [ + 0, + 0, + 0 + ], + "grad": [ + 0.0, + 0.0, + 0.0 + ], + "hess": [ + 0.0, + 0.0, + 0.0 + ], + "values": [ + -0.0, + -0.0, + -0.0 + ], + "bounded": [ + -0.0, + -0.0, + -0.0 + ] + } + ], + "two_rounds": [ + { + "raw": [ + 3.3600001335144043, + 3.3600001335144043 + ], + "second_gradient": [ + 0.7999999523162842, + -3.6000001430511475 + ] + }, + { + "raw": [ + 1.0, + 1.0 + ], + "second_gradient": [ + -1.5, + -10.5 + ] + } + ], + "scope": "GPU primitive composition; CPU trainer tested separately; not assembled GPU Booster" + }, + "levelwise_builder": { + "device_cache_survives_owner_release": true, + "compact_transfer_calls": 85, + "compact_transfer_bytes": 2380, + "two_channel_cases": [ + { + "samples": 16, + "seed": 127, + "clipped": false, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.376172378638702, + "crps": 0.5607174393426145 + }, + "cuda": { + "nll": 1.3761723780246782, + "crps": 0.5607174390134131 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 16, + "seed": 127, + "clipped": true, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.4545325936069222, + "crps": 0.6108751652432844 + }, + "cuda": { + "nll": 1.4545325948769345, + "crps": 0.6108751656755558 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 4097, + "seed": 127, + "clipped": false, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.339077446639963, + "crps": 0.5416246632667225 + }, + "cuda": { + "nll": 1.3390774100065654, + "crps": 0.5416246370616199 + }, + "max_raw_error": 1.9371509552001953e-07 + }, + { + "samples": 4097, + "seed": 127, + "clipped": true, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.5450022972468154, + "crps": 0.6447824943510968 + }, + "cuda": { + "nll": 1.5450022976836335, + "crps": 0.6447824953605893 + }, + "max_raw_error": 1.4901161193847656e-08 + } + ], + "cpu_load_prediction": true, + "scope": "direct GPU builder composition; GPU Booster.fit remains P5; named transfers only" + }, + "strict_extension_trainer": { + "adapter_cases": [ + { + "distribution": "normal", + "natural": false, + "target_sha256": "e53a62533eed353d90ace7744347822d8a31b9dc2ad4e93af74c6195a608f32a", + "max_raw_error": 1.1920928955078125e-07 + }, + { + "distribution": "normal", + "natural": true, + "target_sha256": "e53a62533eed353d90ace7744347822d8a31b9dc2ad4e93af74c6195a608f32a", + "max_raw_error": 1.1920928955078125e-07 + }, + { + "distribution": "poisson", + "natural": false, + "target_sha256": "90d27f84e5a24dfaa5d3c66768b05130e46842a9b3b3fd6b01dd35ec59f0af6c", + "max_raw_error": 5.960464477539063e-08 + }, + { + "distribution": "poisson", + "natural": true, + "target_sha256": "90d27f84e5a24dfaa5d3c66768b05130e46842a9b3b3fd6b01dd35ec59f0af6c", + "max_raw_error": 5.960464477539063e-08 + } + ], + "additional_invalid_statistics": 3, + "actual_fit": true, + "legacy_dispatch_blocked": true, + "rollback": true, + "cpu_load_prediction": true, + "cases": [ + { + "explicit_builder": false, + "fit_report": { + "requested_device": "cuda", + "actual_device": "cuda", + "binning_device": "cpu", + "initialization_device": "cpu", + "objective_device": "cuda", + "tree_device": "cuda", + "update_device": "cuda", + "eval_device": null, + "builder_path": "LevelWiseBuilder", + "fallback_reason": null, + "random_state": 7, + "tree_counts": { + "loc": 2, + "scale": 2 + }, + "timing_scope": "No performance timing collected", + "transfer_scope": "one-time binned/y/weights upload; compact tree finalization downloads; device-only defensive input copies and cache verification; scalar validation syncs; predict_raw uses CPU trees; no profiler proof" + }, + "max_raw_error": 1.1920928955078125e-07, + "cpu_crps": 0.5792920059096566, + "cuda_crps": 0.5792920073406618, + "cpu_nll": 1.462673544883728, + "cuda_nll": 1.462673544883728 + }, + { + "explicit_builder": true, + "fit_report": { + "requested_device": "cuda", + "actual_device": "cuda", + "binning_device": "cpu", + "initialization_device": "cpu", + "objective_device": "cuda", + "tree_device": "cuda", + "update_device": "cuda", + "eval_device": null, + "builder_path": "ExternalBuilder", + "fallback_reason": null, + "random_state": 7, + "tree_counts": { + "loc": 2, + "scale": 2 + }, + "timing_scope": "No performance timing collected", + "transfer_scope": "one-time binned/y/weights upload; compact tree finalization downloads; device-only defensive input copies and cache verification; scalar validation syncs; predict_raw uses CPU trees; no profiler proof" + }, + "max_raw_error": 5.960464477539063e-08, + "cpu_crps": 0.5792920059096566, + "cuda_crps": 0.57929200316153, + "cpu_nll": 1.462673544883728, + "cuda_nll": 1.462673544883728 + } + ], + "compact_transfer_calls": 40, + "compact_transfer_bytes": 1120, + "data_sha256": "2b189618a4b1b0665498afa0ecd6abbe11e1d0231706995d747b65ad783ced6e", + "samples": 257, + "seed": 137, + "nsys_available": false, + "profiler_trace_collected": false, + "scope": "named transfer wrappers only; scalar syncs and device defensive copies allowed" + } + }, + "remote_function_wall_s": 49.665708003999995, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T180943Z-e145df4a/README.md b/benchmarks/results/foundation/20260905T180943Z-e145df4a/README.md new file mode 100644 index 0000000..0b4189e --- /dev/null +++ b/benchmarks/results/foundation/20260905T180943Z-e145df4a/README.md @@ -0,0 +1,17 @@ +# Failed P6 GPU public-demo process run + +Source: `5d10b77347b8c578dfa0b8fc3f97f3b78fbc1531`. +The suite returned **1 failed / 2 passed / 0 skipped**; it is not passing P6 evidence. + +The independent GPU math assertions and all eight CPU/CUDA composition cells +completed before the test reached the public demo subprocess. That subprocess +failed during CUDA availability discovery with a multiprocessing traceback and +`CUDA backend requested but CUDA is not available`. The demo executed fitting +at module top level without a main guard, allowing spawned interpreter re-entry. +Add a normal `if __name__ == '__main__'` entry guard and a no-side-effect import +check; rerun the whole package conformance sequence. No metric tolerance changes. + +The final checks dictionary was not written because the test had not completed; +no uninstall/inference check ran. The raw failing JUnit/stdout is retained, with +manifest/wheel/source/environment provenance. No claim of GPU package completion, +quality, speed or adoption follows from this failed run. diff --git a/benchmarks/results/foundation/20260905T180943Z-e145df4a/junit.xml b/benchmarks/results/foundation/20260905T180943Z-e145df4a/junit.xml new file mode 100644 index 0000000..4102a1a --- /dev/null +++ b/benchmarks/results/foundation/20260905T180943Z-e145df4a/junit.xml @@ -0,0 +1,293 @@ +checks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 46, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...} +monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x2a6220e8bf80> + + def test_installed_gpu_extensions(checks, monkeypatch): + import bounded_leaves + import cupy as cp + import normal_fisher + from bounded_leaves import BoundedNewton + from normal_fisher import ChannelDecay, NormalFisher + from scipy.special import ndtr + + from openboost.experimental import Booster, ExecutionContext, LevelWiseBuilder, TrainerConfig + + manifest = json.loads(Path("manifest.json").read_text()) + installed = {} + for module, distname in [ + (normal_fisher, "openboost-example-normal-fisher"), + (bounded_leaves, "openboost-example-bounded-leaves"), + ]: + location = Path(module.__file__) + assert "site-packages" in location.parts + dist = importlib.metadata.distribution(distname) + wheel = next( + n + for n in manifest["extension_wheels"] + if n.startswith(distname.replace("-", "_") + "-") + ) + with zipfile.ZipFile(wheel) as archive: + count = 0 + for name in archive.namelist(): + if name.endswith(".py"): + assert archive.read(name) == Path(dist.locate_file(name)).read_bytes() + count += 1 + assert count > 0 + installed[module.__name__] = { + "version": dist.version, + "verified_python_files": count, + "path": str(location.relative_to(sys.prefix)), + } + obj = NormalFisher() + ctx = ExecutionContext("cuda", cp, np.random.default_rng(7), 0) + y = np.array([-1, 2, 4], np.float32) + w = np.array([0, 0.5, 2], np.float32) + raw = { + "mu": np.array([0.2, 0.4, 0.1], np.float32), + "log_sigma": np.array([-0.3, 0.2, 0.5], np.float32), + } + stats = obj.step( + {k: cp.asarray(v) for k, v in raw.items()}, cp.asarray(y), cp.asarray(w), context=ctx + ) + + def nll(values): + return w * ( + values["log_sigma"] + + 0.5 * ((y - values["mu"]) * np.exp(-values["log_sigma"])) ** 2 + + 0.5 * np.log(2 * np.pi) + ) + + for k in raw: + plus, minus = ({j: a.astype(float) for j, a in raw.items()} for _ in range(2)) + plus[k] += 1e-5 + minus[k] -= 1e-5 + np.testing.assert_allclose( + cp.asnumpy(stats[k][0]), (nll(plus) - nll(minus)) / 2e-5, atol=1e-6, rtol=2e-6 + ) + assert all(isinstance(a, cp.ndarray) and a.dtype == cp.float32 for a in stats[k]) + np.testing.assert_allclose( + cp.asnumpy(stats["mu"][1]), w * np.exp(-2 * raw["log_sigma"]), rtol=1e-6 + ) + np.testing.assert_array_equal(cp.asnumpy(stats["log_sigma"][1]), 2 * w) + gpu_raw = {k: cp.asarray(v) for k, v in raw.items()} + np.testing.assert_allclose( + obj.loss_value(gpu_raw, cp.asarray(y), cp.asarray(w), context=ctx), + nll({k: v.astype(float) for k, v in raw.items()}).sum() / w.sum(), + ) + constrained = obj.constrain(gpu_raw) + assert isinstance(constrained["sigma"], cp.ndarray) + np.testing.assert_allclose( + cp.asnumpy(constrained["sigma"]), np.exp(raw["log_sigma"].astype(float)) + ) + with pytest.raises(ValueError, match="device"): + obj.step(raw, y, w, context=ctx) + for logs in (-1000, 1000): + with pytest.raises(ValueError): + obj.step( + {"mu": cp.ones(2, cp.float32), "log_sigma": cp.full(2, logs, cp.float32)}, + cp.ones(2, cp.float32), + context=ctx, + ) + + class RecordedNormal(NormalFisher): + def __init__(self): + self.gradients = [] + + def step(self, *args, **kwargs): + out = super().step(*args, **kwargs) + self.gradients.append(out["mu"][0].copy()) + return out + + copy_to_host = cp.asnumpy + downloads = [] + records = [] + saved = Path("gpu_saved") + saved.mkdir(exist_ok=True) + for samples in (16, 4097): + rng = np.random.default_rng(149) + X = rng.normal(size=(samples, 3)).astype(np.float32) + y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=samples)).astype(np.float32) + weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), samples) + outputs, next_gradients = {}, {} + cfg = TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.2, n_bins=32, random_state=7) + for bounded, scheduled in [(False, False), (False, True), (True, False), (True, True)]: + result = [] + for device in ("cpu", "cuda"): + objective = RecordedNormal() + model = Booster( + objective=objective, + tree_builder=LevelWiseBuilder( + leaf_rule=BoundedNewton(0.1) if bounded else None + ), + step_schedule=ChannelDecay() if scheduled else None, + config=cfg, + device=device, + ) + + def compact_only(a, *args, **kwargs): + assert a.shape == (7,) and a.dtype in (cp.int32, cp.float32) + downloads.append(a.nbytes) + return copy_to_host(a, *args, **kwargs) + + with monkeypatch.context() as m: + if device == "cuda": + m.setattr(cp, "asnumpy", compact_only) + model.fit(X, y, sample_weight=weights) + prediction = model.predict_raw(X) + result.append(prediction) + if device == "cuda": + assert model.fit_report_["actual_device"] == "cuda" + assert model.fit_report_["fallback_reason"] is None + if scheduled: + assert model.coefficients_ == {"mu": [0.2, 0.1], "log_sigma": [0.1, 0.05]} + if bounded: + assert all( + np.max(np.abs(tree.values)) <= 0.100001 + for trees in model.trees_.values() + for tree in trees + ) + next_gradients[bounded, scheduled] = copy_to_host(objective.gradients[1]) + outputs[bounded, scheduled] = prediction + path = saved / f"{samples}-{bounded}-{scheduled}.ob" + model.save(path) + np.savez(path.with_suffix(".npz"), X=X, **prediction) + for channel in result[0]: + np.testing.assert_allclose( + result[1][channel], result[0][channel], atol=2e-5, rtol=2e-5 + ) + + def metrics(pred, y=y, weights=weights): + sigma = np.exp(pred["log_sigma"].astype(float)) + z = (y - pred["mu"]) / sigma + return { + "nll": float( + np.average( + pred["log_sigma"] + 0.5 * z * z + 0.5 * np.log(2 * np.pi), + weights=weights, + ) + ), + "crps": float( + np.average( + sigma + * ( + z * (2 * ndtr(z) - 1) + + 2 * np.exp(-z * z / 2) / np.sqrt(2 * np.pi) + - 1 / np.sqrt(np.pi) + ), + weights=weights, + ) + ), + } + + cpu_metrics, gpu_metrics = metrics(result[0]), metrics(result[1]) + for metric in cpu_metrics: + np.testing.assert_allclose( + gpu_metrics[metric], cpu_metrics[metric], atol=2e-5, rtol=2e-5 + ) + records.append( + { + "samples": samples, + "seed": 149, + "bounded": bounded, + "scheduled": scheduled, + "data_sha256": hashlib.sha256( + X.tobytes() + y.tobytes() + weights.tobytes() + ).hexdigest(), + "cpu": cpu_metrics, + "cuda": gpu_metrics, + "max_raw_error": max( + float(np.max(np.abs(result[0][k] - result[1][k]))) for k in result[0] + ), + } + ) + assert not np.allclose(outputs[False, False]["mu"], outputs[False, True]["mu"]) + assert not np.allclose(outputs[False, True]["mu"], outputs[True, True]["mu"]) + assert not np.allclose(next_gradients[False, True], next_gradients[True, True]) + assert len(downloads) == 160 +> demo = subprocess.run( + [sys.executable, "extension_demo.py", "--device", "cuda"], + check=True, + text=True, + capture_output=True, + ) + +test_extensions.py:219: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + +input = None, capture_output = True, timeout = None, check = True +popenargs = (['/usr/local/bin/python', 'extension_demo.py', '--device', 'cuda'],) +kwargs = {'stderr': -1, 'stdout': -1, 'text': True} +process = <Popen: returncode: 1 args: ['/usr/local/bin/python', 'extension_demo.py', '...> +stdout = '' +stderr = 'Traceback (most recent call last):\n File "<string>", line 1, in <module>\n File "/usr/local/lib/python3.12/multipr...("CUDA backend requested but CUDA is not available")\nRuntimeError: CUDA backend requested but CUDA is not available\n' +retcode = 1 + + def run(*popenargs, + input=None, capture_output=False, timeout=None, check=False, **kwargs): + """Run command with arguments and return a CompletedProcess instance. + + The returned instance will have attributes args, returncode, stdout and + stderr. By default, stdout and stderr are not captured, and those attributes + will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them, + or pass capture_output=True to capture both. + + If check is True and the exit code was non-zero, it raises a + CalledProcessError. The CalledProcessError object will have the return code + in the returncode attribute, and output & stderr attributes if those streams + were captured. + + If timeout is given, and the process takes too long, a TimeoutExpired + exception will be raised. + + There is an optional argument "input", allowing you to + pass bytes or a string to the subprocess's stdin. If you use this argument + you may not also use the Popen constructor's "stdin" argument, as + it will be used internally. + + By default, all communication is in bytes, and therefore any "input" should + be bytes, and the stdout and stderr will be bytes. If in text mode, any + "input" should be a string, and stdout and stderr will be strings decoded + according to locale encoding, or by "encoding" if set. Text mode is + triggered by setting any of text, encoding, errors or universal_newlines. + + The other arguments are the same as for the Popen constructor. + """ + if input is not None: + if kwargs.get('stdin') is not None: + raise ValueError('stdin and input arguments may not both be used.') + kwargs['stdin'] = PIPE + + if capture_output: + if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None: + raise ValueError('stdout and stderr arguments may not be used ' + 'with capture_output.') + kwargs['stdout'] = PIPE + kwargs['stderr'] = PIPE + + with Popen(*popenargs, **kwargs) as process: + try: + stdout, stderr = process.communicate(input, timeout=timeout) + except TimeoutExpired as exc: + process.kill() + if _mswindows: + # Windows accumulates the output in a single blocking + # read() call run on child threads, with the timeout + # being done in a join() on those threads. communicate() + # _after_ kill() is required to collect that and add it + # to the exception. + exc.stdout, exc.stderr = process.communicate() + else: + # POSIX _communicate already populated the output so + # far into the TimeoutExpired exception. + process.wait() + raise + except: # Including KeyboardInterrupt, communicate handled that. + process.kill() + # We don't call process.wait() as .__exit__ does that for us. + raise + retcode = process.poll() + if check and retcode: +> raise CalledProcessError(retcode, process.args, + output=stdout, stderr=stderr) +E subprocess.CalledProcessError: Command '['/usr/local/bin/python', 'extension_demo.py', '--device', 'cuda']' returned non-zero exit status 1. + +/usr/local/lib/python3.12/subprocess.py:571: CalledProcessError \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T180943Z-e145df4a/manifest.json b/benchmarks/results/foundation/20260905T180943Z-e145df4a/manifest.json new file mode 100644 index 0000000..9b86b8e --- /dev/null +++ b/benchmarks/results/foundation/20260905T180943Z-e145df4a/manifest.json @@ -0,0 +1,61 @@ +{ + "schema_version": 1, + "suite": "extensions", + "test_files": [ + "test_smoke.py", + "test_extensions.py" + ], + "source_sha": "5d10b77347b8c578dfa0b8fc3f97f3b78fbc1531", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_extensions.py": "ce25dd5d90b02d0f3d755084792c8c1666920a9e1350e88e0c982606d750b232", + "check_extension_inference.py": "24dcbcd2e3ed392b91b51ce228e9ac2ed4eb9e8797941854e47558dbd062104e", + "extension_demo.py": "99e97b0267e5df872580e0fb4506915b59f7cc63fb18ea77ee68280cc2f477dc" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_extensions" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "extension_wheels": { + "openboost_example_normal_fisher-0.2.0-py3-none-any.whl": "47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b", + "openboost_example_bounded_leaves-0.2.0-py3-none-any.whl": "dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3" + }, + "extension_sources": { + "examples/extensions/normal_fisher/README.md": "78a5142eaf92a81df9c5908861a1ce66e5ae8b03bf753e0b84a834c5f2a5d0eb", + "examples/extensions/normal_fisher/pyproject.toml": "ab70495fafa430dfb1377a873cbb821c9484c9008cea26575fa3c3fa2a91e40d", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "cabafae48d9ad251eea62230b1d38ba7407fbb113dec116b3e840c05e032afd3", + "examples/extensions/normal_fisher/tests/test_normal.py": "6dcb48f9434879b054fe9a9e8b6fca5f4ba01782d8c45ed4c566a8306ee867c3", + "examples/extensions/bounded_leaves/README.md": "749f68fafb9e8cb5911c16aa79ab7a82593599e5c196c20be3ec9923172144e9", + "examples/extensions/bounded_leaves/pyproject.toml": "8731c3fa03f03341fcc987b449c04658c5bde3e7a4d24772e3bc2199ceb55eac", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "d53927c63c71ea3cf37083e0d71d11aff58941dbed1df430838c2d6b2912efff", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "41339c49f146181811dd0680d2b1abc563448ce35b4a79e8d4f0559c5d949baf" + }, + "run_id": "20260905T180943Z-e145df4a", + "modal_image_id": "im-pzMdrigkc8oXxE7HG4SGK8" +} diff --git a/benchmarks/results/foundation/20260905T180943Z-e145df4a/results.json b/benchmarks/results/foundation/20260905T180943Z-e145df4a/results.json new file mode 100644 index 0000000..6584f35 --- /dev/null +++ b/benchmarks/results/foundation/20260905T180943Z-e145df4a/results.json @@ -0,0 +1,94 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 266817929216, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "openboost": "1.0.0rc1", + "cuda-pathfinder": "1.4.0", + "cuda-bindings": "13.1.1", + "scipy": "1.16.3", + "Pygments": "2.19.2", + "packaging": "25.0", + "openboost-example-normal-fisher": "0.2.0", + "llvmlite": "0.46.0", + "numpy": "2.3.5", + "execnet": "2.1.2", + "coverage": "7.13.1", + "openboost-example-bounded-leaves": "0.2.0", + "pytest": "9.0.2", + "joblib": "1.5.3", + "numba-cuda": "0.27.0", + "setuptools": "69.0.3", + "iniconfig": "2.3.0", + "pluggy": "1.6.0", + "pytest-xdist": "3.8.0", + "numba": "0.63.1", + "pytest-cov": "7.0.0", + "cupy-cuda12x": "13.6.0", + "cuda-core": "0.6.0", + "pip": "23.3.2", + "fastrlock": "0.8.3", + "protobuf": "6.31.1", + "frozenlist": "1.6.0", + "h2": "4.2.0", + "cbor2": "5.7.0", + "aiohttp": "3.12.7", + "propcache": "0.3.1", + "attrs": "25.3.0", + "multidict": "6.4.4", + "grpclib": "0.4.8", + "certifi": "2025.4.26", + "typing_extensions": "4.13.2", + "idna": "3.10", + "aiohappyeyeballs": "2.6.1", + "yarl": "1.20.0", + "hpack": "4.1.0", + "hyperframe": "6.1.0", + "aiosignal": "1.3.2" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "5d10b77347b8c578dfa0b8fc3f97f3b78fbc1531", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_extensions.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 1, + "stdout": "..F [100%]\n=================================== FAILURES ===================================\n________________________ test_installed_gpu_extensions _________________________\n\nchecks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 46, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\nmonkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x2a6220e8bf80>\n\n def test_installed_gpu_extensions(checks, monkeypatch):\n import bounded_leaves\n import cupy as cp\n import normal_fisher\n from bounded_leaves import BoundedNewton\n from normal_fisher import ChannelDecay, NormalFisher\n from scipy.special import ndtr\n \n from openboost.experimental import Booster, ExecutionContext, LevelWiseBuilder, TrainerConfig\n \n manifest = json.loads(Path(\"manifest.json\").read_text())\n installed = {}\n for module, distname in [\n (normal_fisher, \"openboost-example-normal-fisher\"),\n (bounded_leaves, \"openboost-example-bounded-leaves\"),\n ]:\n location = Path(module.__file__)\n assert \"site-packages\" in location.parts\n dist = importlib.metadata.distribution(distname)\n wheel = next(\n n\n for n in manifest[\"extension_wheels\"]\n if n.startswith(distname.replace(\"-\", \"_\") + \"-\")\n )\n with zipfile.ZipFile(wheel) as archive:\n count = 0\n for name in archive.namelist():\n if name.endswith(\".py\"):\n assert archive.read(name) == Path(dist.locate_file(name)).read_bytes()\n count += 1\n assert count > 0\n installed[module.__name__] = {\n \"version\": dist.version,\n \"verified_python_files\": count,\n \"path\": str(location.relative_to(sys.prefix)),\n }\n obj = NormalFisher()\n ctx = ExecutionContext(\"cuda\", cp, np.random.default_rng(7), 0)\n y = np.array([-1, 2, 4], np.float32)\n w = np.array([0, 0.5, 2], np.float32)\n raw = {\n \"mu\": np.array([0.2, 0.4, 0.1], np.float32),\n \"log_sigma\": np.array([-0.3, 0.2, 0.5], np.float32),\n }\n stats = obj.step(\n {k: cp.asarray(v) for k, v in raw.items()}, cp.asarray(y), cp.asarray(w), context=ctx\n )\n \n def nll(values):\n return w * (\n values[\"log_sigma\"]\n + 0.5 * ((y - values[\"mu\"]) * np.exp(-values[\"log_sigma\"])) ** 2\n + 0.5 * np.log(2 * np.pi)\n )\n \n for k in raw:\n plus, minus = ({j: a.astype(float) for j, a in raw.items()} for _ in range(2))\n plus[k] += 1e-5\n minus[k] -= 1e-5\n np.testing.assert_allclose(\n cp.asnumpy(stats[k][0]), (nll(plus) - nll(minus)) / 2e-5, atol=1e-6, rtol=2e-6\n )\n assert all(isinstance(a, cp.ndarray) and a.dtype == cp.float32 for a in stats[k])\n np.testing.assert_allclose(\n cp.asnumpy(stats[\"mu\"][1]), w * np.exp(-2 * raw[\"log_sigma\"]), rtol=1e-6\n )\n np.testing.assert_array_equal(cp.asnumpy(stats[\"log_sigma\"][1]), 2 * w)\n gpu_raw = {k: cp.asarray(v) for k, v in raw.items()}\n np.testing.assert_allclose(\n obj.loss_value(gpu_raw, cp.asarray(y), cp.asarray(w), context=ctx),\n nll({k: v.astype(float) for k, v in raw.items()}).sum() / w.sum(),\n )\n constrained = obj.constrain(gpu_raw)\n assert isinstance(constrained[\"sigma\"], cp.ndarray)\n np.testing.assert_allclose(\n cp.asnumpy(constrained[\"sigma\"]), np.exp(raw[\"log_sigma\"].astype(float))\n )\n with pytest.raises(ValueError, match=\"device\"):\n obj.step(raw, y, w, context=ctx)\n for logs in (-1000, 1000):\n with pytest.raises(ValueError):\n obj.step(\n {\"mu\": cp.ones(2, cp.float32), \"log_sigma\": cp.full(2, logs, cp.float32)},\n cp.ones(2, cp.float32),\n context=ctx,\n )\n \n class RecordedNormal(NormalFisher):\n def __init__(self):\n self.gradients = []\n \n def step(self, *args, **kwargs):\n out = super().step(*args, **kwargs)\n self.gradients.append(out[\"mu\"][0].copy())\n return out\n \n copy_to_host = cp.asnumpy\n downloads = []\n records = []\n saved = Path(\"gpu_saved\")\n saved.mkdir(exist_ok=True)\n for samples in (16, 4097):\n rng = np.random.default_rng(149)\n X = rng.normal(size=(samples, 3)).astype(np.float32)\n y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=samples)).astype(np.float32)\n weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), samples)\n outputs, next_gradients = {}, {}\n cfg = TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.2, n_bins=32, random_state=7)\n for bounded, scheduled in [(False, False), (False, True), (True, False), (True, True)]:\n result = []\n for device in (\"cpu\", \"cuda\"):\n objective = RecordedNormal()\n model = Booster(\n objective=objective,\n tree_builder=LevelWiseBuilder(\n leaf_rule=BoundedNewton(0.1) if bounded else None\n ),\n step_schedule=ChannelDecay() if scheduled else None,\n config=cfg,\n device=device,\n )\n \n def compact_only(a, *args, **kwargs):\n assert a.shape == (7,) and a.dtype in (cp.int32, cp.float32)\n downloads.append(a.nbytes)\n return copy_to_host(a, *args, **kwargs)\n \n with monkeypatch.context() as m:\n if device == \"cuda\":\n m.setattr(cp, \"asnumpy\", compact_only)\n model.fit(X, y, sample_weight=weights)\n prediction = model.predict_raw(X)\n result.append(prediction)\n if device == \"cuda\":\n assert model.fit_report_[\"actual_device\"] == \"cuda\"\n assert model.fit_report_[\"fallback_reason\"] is None\n if scheduled:\n assert model.coefficients_ == {\"mu\": [0.2, 0.1], \"log_sigma\": [0.1, 0.05]}\n if bounded:\n assert all(\n np.max(np.abs(tree.values)) <= 0.100001\n for trees in model.trees_.values()\n for tree in trees\n )\n next_gradients[bounded, scheduled] = copy_to_host(objective.gradients[1])\n outputs[bounded, scheduled] = prediction\n path = saved / f\"{samples}-{bounded}-{scheduled}.ob\"\n model.save(path)\n np.savez(path.with_suffix(\".npz\"), X=X, **prediction)\n for channel in result[0]:\n np.testing.assert_allclose(\n result[1][channel], result[0][channel], atol=2e-5, rtol=2e-5\n )\n \n def metrics(pred, y=y, weights=weights):\n sigma = np.exp(pred[\"log_sigma\"].astype(float))\n z = (y - pred[\"mu\"]) / sigma\n return {\n \"nll\": float(\n np.average(\n pred[\"log_sigma\"] + 0.5 * z * z + 0.5 * np.log(2 * np.pi),\n weights=weights,\n )\n ),\n \"crps\": float(\n np.average(\n sigma\n * (\n z * (2 * ndtr(z) - 1)\n + 2 * np.exp(-z * z / 2) / np.sqrt(2 * np.pi)\n - 1 / np.sqrt(np.pi)\n ),\n weights=weights,\n )\n ),\n }\n \n cpu_metrics, gpu_metrics = metrics(result[0]), metrics(result[1])\n for metric in cpu_metrics:\n np.testing.assert_allclose(\n gpu_metrics[metric], cpu_metrics[metric], atol=2e-5, rtol=2e-5\n )\n records.append(\n {\n \"samples\": samples,\n \"seed\": 149,\n \"bounded\": bounded,\n \"scheduled\": scheduled,\n \"data_sha256\": hashlib.sha256(\n X.tobytes() + y.tobytes() + weights.tobytes()\n ).hexdigest(),\n \"cpu\": cpu_metrics,\n \"cuda\": gpu_metrics,\n \"max_raw_error\": max(\n float(np.max(np.abs(result[0][k] - result[1][k]))) for k in result[0]\n ),\n }\n )\n assert not np.allclose(outputs[False, False][\"mu\"], outputs[False, True][\"mu\"])\n assert not np.allclose(outputs[False, True][\"mu\"], outputs[True, True][\"mu\"])\n assert not np.allclose(next_gradients[False, True], next_gradients[True, True])\n assert len(downloads) == 160\n> demo = subprocess.run(\n [sys.executable, \"extension_demo.py\", \"--device\", \"cuda\"],\n check=True,\n text=True,\n capture_output=True,\n )\n\ntest_extensions.py:219: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\ninput = None, capture_output = True, timeout = None, check = True\npopenargs = (['/usr/local/bin/python', 'extension_demo.py', '--device', 'cuda'],)\nkwargs = {'stderr': -1, 'stdout': -1, 'text': True}\nprocess = \nstdout = ''\nstderr = 'Traceback (most recent call last):\\n File \"\", line 1, in \\n File \"/usr/local/lib/python3.12/multipr...(\"CUDA backend requested but CUDA is not available\")\\nRuntimeError: CUDA backend requested but CUDA is not available\\n'\nretcode = 1\n\n def run(*popenargs,\n input=None, capture_output=False, timeout=None, check=False, **kwargs):\n \"\"\"Run command with arguments and return a CompletedProcess instance.\n \n The returned instance will have attributes args, returncode, stdout and\n stderr. By default, stdout and stderr are not captured, and those attributes\n will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them,\n or pass capture_output=True to capture both.\n \n If check is True and the exit code was non-zero, it raises a\n CalledProcessError. The CalledProcessError object will have the return code\n in the returncode attribute, and output & stderr attributes if those streams\n were captured.\n \n If timeout is given, and the process takes too long, a TimeoutExpired\n exception will be raised.\n \n There is an optional argument \"input\", allowing you to\n pass bytes or a string to the subprocess's stdin. If you use this argument\n you may not also use the Popen constructor's \"stdin\" argument, as\n it will be used internally.\n \n By default, all communication is in bytes, and therefore any \"input\" should\n be bytes, and the stdout and stderr will be bytes. If in text mode, any\n \"input\" should be a string, and stdout and stderr will be strings decoded\n according to locale encoding, or by \"encoding\" if set. Text mode is\n triggered by setting any of text, encoding, errors or universal_newlines.\n \n The other arguments are the same as for the Popen constructor.\n \"\"\"\n if input is not None:\n if kwargs.get('stdin') is not None:\n raise ValueError('stdin and input arguments may not both be used.')\n kwargs['stdin'] = PIPE\n \n if capture_output:\n if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:\n raise ValueError('stdout and stderr arguments may not be used '\n 'with capture_output.')\n kwargs['stdout'] = PIPE\n kwargs['stderr'] = PIPE\n \n with Popen(*popenargs, **kwargs) as process:\n try:\n stdout, stderr = process.communicate(input, timeout=timeout)\n except TimeoutExpired as exc:\n process.kill()\n if _mswindows:\n # Windows accumulates the output in a single blocking\n # read() call run on child threads, with the timeout\n # being done in a join() on those threads. communicate()\n # _after_ kill() is required to collect that and add it\n # to the exception.\n exc.stdout, exc.stderr = process.communicate()\n else:\n # POSIX _communicate already populated the output so\n # far into the TimeoutExpired exception.\n process.wait()\n raise\n except: # Including KeyboardInterrupt, communicate handled that.\n process.kill()\n # We don't call process.wait() as .__exit__ does that for us.\n raise\n retcode = process.poll()\n if check and retcode:\n> raise CalledProcessError(retcode, process.args,\n output=stdout, stderr=stderr)\nE subprocess.CalledProcessError: Command '['/usr/local/bin/python', 'extension_demo.py', '--device', 'cuda']' returned non-zero exit status 1.\n\n/usr/local/lib/python3.12/subprocess.py:571: CalledProcessError\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_extensions.py::test_installed_gpu_extensions\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n=========================== short test summary info ============================\nFAILED test_extensions.py::test_installed_gpu_extensions - subprocess.CalledP...\n1 failed, 2 passed, 18 warnings in 32.46s\n", + "stderr": "", + "junit": "checks = {'dataset_sha256': 'a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec', 'device_objective_calls': 2, 'installed_files_verified': 46, 'installed_module': '/usr/local/lib/python3.12/site-packages/openboost/__init__.py', ...}\nmonkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x2a6220e8bf80>\n\n def test_installed_gpu_extensions(checks, monkeypatch):\n import bounded_leaves\n import cupy as cp\n import normal_fisher\n from bounded_leaves import BoundedNewton\n from normal_fisher import ChannelDecay, NormalFisher\n from scipy.special import ndtr\n \n from openboost.experimental import Booster, ExecutionContext, LevelWiseBuilder, TrainerConfig\n \n manifest = json.loads(Path(\"manifest.json\").read_text())\n installed = {}\n for module, distname in [\n (normal_fisher, \"openboost-example-normal-fisher\"),\n (bounded_leaves, \"openboost-example-bounded-leaves\"),\n ]:\n location = Path(module.__file__)\n assert \"site-packages\" in location.parts\n dist = importlib.metadata.distribution(distname)\n wheel = next(\n n\n for n in manifest[\"extension_wheels\"]\n if n.startswith(distname.replace(\"-\", \"_\") + \"-\")\n )\n with zipfile.ZipFile(wheel) as archive:\n count = 0\n for name in archive.namelist():\n if name.endswith(\".py\"):\n assert archive.read(name) == Path(dist.locate_file(name)).read_bytes()\n count += 1\n assert count > 0\n installed[module.__name__] = {\n \"version\": dist.version,\n \"verified_python_files\": count,\n \"path\": str(location.relative_to(sys.prefix)),\n }\n obj = NormalFisher()\n ctx = ExecutionContext(\"cuda\", cp, np.random.default_rng(7), 0)\n y = np.array([-1, 2, 4], np.float32)\n w = np.array([0, 0.5, 2], np.float32)\n raw = {\n \"mu\": np.array([0.2, 0.4, 0.1], np.float32),\n \"log_sigma\": np.array([-0.3, 0.2, 0.5], np.float32),\n }\n stats = obj.step(\n {k: cp.asarray(v) for k, v in raw.items()}, cp.asarray(y), cp.asarray(w), context=ctx\n )\n \n def nll(values):\n return w * (\n values[\"log_sigma\"]\n + 0.5 * ((y - values[\"mu\"]) * np.exp(-values[\"log_sigma\"])) ** 2\n + 0.5 * np.log(2 * np.pi)\n )\n \n for k in raw:\n plus, minus = ({j: a.astype(float) for j, a in raw.items()} for _ in range(2))\n plus[k] += 1e-5\n minus[k] -= 1e-5\n np.testing.assert_allclose(\n cp.asnumpy(stats[k][0]), (nll(plus) - nll(minus)) / 2e-5, atol=1e-6, rtol=2e-6\n )\n assert all(isinstance(a, cp.ndarray) and a.dtype == cp.float32 for a in stats[k])\n np.testing.assert_allclose(\n cp.asnumpy(stats[\"mu\"][1]), w * np.exp(-2 * raw[\"log_sigma\"]), rtol=1e-6\n )\n np.testing.assert_array_equal(cp.asnumpy(stats[\"log_sigma\"][1]), 2 * w)\n gpu_raw = {k: cp.asarray(v) for k, v in raw.items()}\n np.testing.assert_allclose(\n obj.loss_value(gpu_raw, cp.asarray(y), cp.asarray(w), context=ctx),\n nll({k: v.astype(float) for k, v in raw.items()}).sum() / w.sum(),\n )\n constrained = obj.constrain(gpu_raw)\n assert isinstance(constrained[\"sigma\"], cp.ndarray)\n np.testing.assert_allclose(\n cp.asnumpy(constrained[\"sigma\"]), np.exp(raw[\"log_sigma\"].astype(float))\n )\n with pytest.raises(ValueError, match=\"device\"):\n obj.step(raw, y, w, context=ctx)\n for logs in (-1000, 1000):\n with pytest.raises(ValueError):\n obj.step(\n {\"mu\": cp.ones(2, cp.float32), \"log_sigma\": cp.full(2, logs, cp.float32)},\n cp.ones(2, cp.float32),\n context=ctx,\n )\n \n class RecordedNormal(NormalFisher):\n def __init__(self):\n self.gradients = []\n \n def step(self, *args, **kwargs):\n out = super().step(*args, **kwargs)\n self.gradients.append(out[\"mu\"][0].copy())\n return out\n \n copy_to_host = cp.asnumpy\n downloads = []\n records = []\n saved = Path(\"gpu_saved\")\n saved.mkdir(exist_ok=True)\n for samples in (16, 4097):\n rng = np.random.default_rng(149)\n X = rng.normal(size=(samples, 3)).astype(np.float32)\n y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=samples)).astype(np.float32)\n weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), samples)\n outputs, next_gradients = {}, {}\n cfg = TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.2, n_bins=32, random_state=7)\n for bounded, scheduled in [(False, False), (False, True), (True, False), (True, True)]:\n result = []\n for device in (\"cpu\", \"cuda\"):\n objective = RecordedNormal()\n model = Booster(\n objective=objective,\n tree_builder=LevelWiseBuilder(\n leaf_rule=BoundedNewton(0.1) if bounded else None\n ),\n step_schedule=ChannelDecay() if scheduled else None,\n config=cfg,\n device=device,\n )\n \n def compact_only(a, *args, **kwargs):\n assert a.shape == (7,) and a.dtype in (cp.int32, cp.float32)\n downloads.append(a.nbytes)\n return copy_to_host(a, *args, **kwargs)\n \n with monkeypatch.context() as m:\n if device == \"cuda\":\n m.setattr(cp, \"asnumpy\", compact_only)\n model.fit(X, y, sample_weight=weights)\n prediction = model.predict_raw(X)\n result.append(prediction)\n if device == \"cuda\":\n assert model.fit_report_[\"actual_device\"] == \"cuda\"\n assert model.fit_report_[\"fallback_reason\"] is None\n if scheduled:\n assert model.coefficients_ == {\"mu\": [0.2, 0.1], \"log_sigma\": [0.1, 0.05]}\n if bounded:\n assert all(\n np.max(np.abs(tree.values)) <= 0.100001\n for trees in model.trees_.values()\n for tree in trees\n )\n next_gradients[bounded, scheduled] = copy_to_host(objective.gradients[1])\n outputs[bounded, scheduled] = prediction\n path = saved / f\"{samples}-{bounded}-{scheduled}.ob\"\n model.save(path)\n np.savez(path.with_suffix(\".npz\"), X=X, **prediction)\n for channel in result[0]:\n np.testing.assert_allclose(\n result[1][channel], result[0][channel], atol=2e-5, rtol=2e-5\n )\n \n def metrics(pred, y=y, weights=weights):\n sigma = np.exp(pred[\"log_sigma\"].astype(float))\n z = (y - pred[\"mu\"]) / sigma\n return {\n \"nll\": float(\n np.average(\n pred[\"log_sigma\"] + 0.5 * z * z + 0.5 * np.log(2 * np.pi),\n weights=weights,\n )\n ),\n \"crps\": float(\n np.average(\n sigma\n * (\n z * (2 * ndtr(z) - 1)\n + 2 * np.exp(-z * z / 2) / np.sqrt(2 * np.pi)\n - 1 / np.sqrt(np.pi)\n ),\n weights=weights,\n )\n ),\n }\n \n cpu_metrics, gpu_metrics = metrics(result[0]), metrics(result[1])\n for metric in cpu_metrics:\n np.testing.assert_allclose(\n gpu_metrics[metric], cpu_metrics[metric], atol=2e-5, rtol=2e-5\n )\n records.append(\n {\n \"samples\": samples,\n \"seed\": 149,\n \"bounded\": bounded,\n \"scheduled\": scheduled,\n \"data_sha256\": hashlib.sha256(\n X.tobytes() + y.tobytes() + weights.tobytes()\n ).hexdigest(),\n \"cpu\": cpu_metrics,\n \"cuda\": gpu_metrics,\n \"max_raw_error\": max(\n float(np.max(np.abs(result[0][k] - result[1][k]))) for k in result[0]\n ),\n }\n )\n assert not np.allclose(outputs[False, False][\"mu\"], outputs[False, True][\"mu\"])\n assert not np.allclose(outputs[False, True][\"mu\"], outputs[True, True][\"mu\"])\n assert not np.allclose(next_gradients[False, True], next_gradients[True, True])\n assert len(downloads) == 160\n> demo = subprocess.run(\n [sys.executable, \"extension_demo.py\", \"--device\", \"cuda\"],\n check=True,\n text=True,\n capture_output=True,\n )\n\ntest_extensions.py:219: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\ninput = None, capture_output = True, timeout = None, check = True\npopenargs = (['/usr/local/bin/python', 'extension_demo.py', '--device', 'cuda'],)\nkwargs = {'stderr': -1, 'stdout': -1, 'text': True}\nprocess = <Popen: returncode: 1 args: ['/usr/local/bin/python', 'extension_demo.py', '...>\nstdout = ''\nstderr = 'Traceback (most recent call last):\\n File \"<string>\", line 1, in <module>\\n File \"/usr/local/lib/python3.12/multipr...(\"CUDA backend requested but CUDA is not available\")\\nRuntimeError: CUDA backend requested but CUDA is not available\\n'\nretcode = 1\n\n def run(*popenargs,\n input=None, capture_output=False, timeout=None, check=False, **kwargs):\n \"\"\"Run command with arguments and return a CompletedProcess instance.\n \n The returned instance will have attributes args, returncode, stdout and\n stderr. By default, stdout and stderr are not captured, and those attributes\n will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them,\n or pass capture_output=True to capture both.\n \n If check is True and the exit code was non-zero, it raises a\n CalledProcessError. The CalledProcessError object will have the return code\n in the returncode attribute, and output & stderr attributes if those streams\n were captured.\n \n If timeout is given, and the process takes too long, a TimeoutExpired\n exception will be raised.\n \n There is an optional argument \"input\", allowing you to\n pass bytes or a string to the subprocess's stdin. If you use this argument\n you may not also use the Popen constructor's \"stdin\" argument, as\n it will be used internally.\n \n By default, all communication is in bytes, and therefore any \"input\" should\n be bytes, and the stdout and stderr will be bytes. If in text mode, any\n \"input\" should be a string, and stdout and stderr will be strings decoded\n according to locale encoding, or by \"encoding\" if set. Text mode is\n triggered by setting any of text, encoding, errors or universal_newlines.\n \n The other arguments are the same as for the Popen constructor.\n \"\"\"\n if input is not None:\n if kwargs.get('stdin') is not None:\n raise ValueError('stdin and input arguments may not both be used.')\n kwargs['stdin'] = PIPE\n \n if capture_output:\n if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:\n raise ValueError('stdout and stderr arguments may not be used '\n 'with capture_output.')\n kwargs['stdout'] = PIPE\n kwargs['stderr'] = PIPE\n \n with Popen(*popenargs, **kwargs) as process:\n try:\n stdout, stderr = process.communicate(input, timeout=timeout)\n except TimeoutExpired as exc:\n process.kill()\n if _mswindows:\n # Windows accumulates the output in a single blocking\n # read() call run on child threads, with the timeout\n # being done in a join() on those threads. communicate()\n # _after_ kill() is required to collect that and add it\n # to the exception.\n exc.stdout, exc.stderr = process.communicate()\n else:\n # POSIX _communicate already populated the output so\n # far into the TimeoutExpired exception.\n process.wait()\n raise\n except: # Including KeyboardInterrupt, communicate handled that.\n process.kill()\n # We don't call process.wait() as .__exit__ does that for us.\n raise\n retcode = process.poll()\n if check and retcode:\n> raise CalledProcessError(retcode, process.args,\n output=stdout, stderr=stderr)\nE subprocess.CalledProcessError: Command '['/usr/local/bin/python', 'extension_demo.py', '--device', 'cuda']' returned non-zero exit status 1.\n\n/usr/local/lib/python3.12/subprocess.py:571: CalledProcessError", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2 + }, + "remote_function_wall_s": 36.797827493999996, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T181351Z-1aee9568/README.md b/benchmarks/results/foundation/20260905T181351Z-1aee9568/README.md new file mode 100644 index 0000000..ccb8177 --- /dev/null +++ b/benchmarks/results/foundation/20260905T181351Z-1aee9568/README.md @@ -0,0 +1,74 @@ +# P6 independent GPU wheels: installation and conformance + +Clean source: `43fcda31b70a5970b0e588c1d0e4b836707af90e`. **3 passed / 0 skipped** on real T4: +two existing smoke cases plus the independent GPU package test, covering eight +composed CPU/CUDA cells and the public GPU demo. After pytest exits, both example +packages are uninstalled and a new interpreter verifies **nine exact CPU raw +prediction roundtrips**, with neither plugin importable. + +Wheel SHA256: + +- OpenBoost: `5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed` +- normal_fisher 0.2.0: `47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b` +- bounded_leaves 0.2.0: `dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3` + +Only three wheels, test/demo files and a manifest were uploaded; repository +source was not mounted. Both installed extension modules resolve in site-packages +and match their wheel's Python file byte-for-byte. Package source hashes and +public-import checks accompany the wheel hashes. The core wheel is unchanged +from P5: these methods required no core edit or private OpenBoost import. + +Independent float64 finite-difference weighted NLL and analytic Fisher verify +GPU gradients/curvature on a small nonuniform/zero-weight fixture. Device loss +and constrained parameters agree with reference values. Host inputs in a CUDA +step and extreme-scale underflow/overflow are rejected. The larger experiment +uses 16/4097 rows, seed 149, two channels, two rounds and nonuniform/zero weights. +Each size runs objective only, objective+schedule, objective+bounded leaf, and +all three combined, against CPU fits with the same configuration. + +Maximum CPU/CUDA raw error: 3.5762786865234375e-7. +Maximum NLL difference: 2.3084373301784922e-8. +Maximum CRPS difference: 2.8600352086627367e-8. +Per-cell values and exact generated-data hashes are retained in results.json. +Clipping bounds actual stored leaves and changes next-round mean gradients; +the nonconstant schedule changes predictions. The 64-row public demo runs in a +separate process with actual_device=cuda and coefficients mu=[0.2,0.1], +log_sigma=[0.1,0.05]. All nine GPU-trained saved models survive plugin removal. + +The named cupy.asnumpy spy permits only five compact tree arrays per tree: +160 calls / 4,480 bytes across eight instrumented GPU fits (32 trees). It excludes +the separate demo process and explicit reference conversions. P5 defensive device +copies, compact verification uploads and scalar synchronization remain. This is +not a profiler trace or a whole-process transfer audit. + +The first attempt failed in the public-demo subprocess because top-level training +could be re-entered by spawned CUDA discovery workers. The corrected demo uses +a main guard; a CPU import test checks __mp_main__ causes no training/files. +[The original failure](../20260905T180943Z-e145df4a/README.md) is retained. No data, +seed or parity tolerance was changed to obtain the passing result. + +Environment: Tesla T4, nvidia-smi `Tesla T4, 580.95.05, 15360 MiB`, +CUDA runtime 12090, driver API 13000, Python 3.12.1, +CuPy 13.6.0, NumPy 2.3.5, +Numba 0.63.1 / numba-cuda 0.27.0. +Requested CPU=2, RAM=8192 MiB, threads=2; CPU model unknown. Pytest 36.81 s; +remote function including uninstall/inference 43.29 s. These validation/JIT +wall times are not training benchmarks or billed runtimes. + +Reproduce from the clean source revision: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite extensions +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_extensions +``` + +Validate saved evidence offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T181351Z-1aee9568 +``` + +All source/wheel/lock hashes and JUnit copies were verified; private URL/local-path +scan passed. This establishes technical extension installation/conformance (G3), +not third-party adoption (G5), held-out quality or engineering cost advantage (G4). +Next is P7's preregistered matched-quality timing/memory/value evaluation. diff --git a/benchmarks/results/foundation/20260905T181351Z-1aee9568/junit.xml b/benchmarks/results/foundation/20260905T181351Z-1aee9568/junit.xml new file mode 100644 index 0000000..0f58015 --- /dev/null +++ b/benchmarks/results/foundation/20260905T181351Z-1aee9568/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T181351Z-1aee9568/manifest.json b/benchmarks/results/foundation/20260905T181351Z-1aee9568/manifest.json new file mode 100644 index 0000000..de498d3 --- /dev/null +++ b/benchmarks/results/foundation/20260905T181351Z-1aee9568/manifest.json @@ -0,0 +1,61 @@ +{ + "schema_version": 1, + "suite": "extensions", + "test_files": [ + "test_smoke.py", + "test_extensions.py" + ], + "source_sha": "43fcda31b70a5970b0e588c1d0e4b836707af90e", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_extensions.py": "3db0274563b12349d74b6e6f58f09c6291fe881cdfdae6fa384f614be6fc2bf7", + "check_extension_inference.py": "24dcbcd2e3ed392b91b51ce228e9ac2ed4eb9e8797941854e47558dbd062104e", + "extension_demo.py": "3eb0e67f0f4cd4f30283ff93a3b1a15a6aa6a8d24fd31115888ad45bcac1b8e3" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_extensions" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "extension_wheels": { + "openboost_example_normal_fisher-0.2.0-py3-none-any.whl": "47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b", + "openboost_example_bounded_leaves-0.2.0-py3-none-any.whl": "dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3" + }, + "extension_sources": { + "examples/extensions/normal_fisher/README.md": "78a5142eaf92a81df9c5908861a1ce66e5ae8b03bf753e0b84a834c5f2a5d0eb", + "examples/extensions/normal_fisher/pyproject.toml": "ab70495fafa430dfb1377a873cbb821c9484c9008cea26575fa3c3fa2a91e40d", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "cabafae48d9ad251eea62230b1d38ba7407fbb113dec116b3e840c05e032afd3", + "examples/extensions/normal_fisher/tests/test_normal.py": "6dcb48f9434879b054fe9a9e8b6fca5f4ba01782d8c45ed4c566a8306ee867c3", + "examples/extensions/bounded_leaves/README.md": "749f68fafb9e8cb5911c16aa79ab7a82593599e5c196c20be3ec9923172144e9", + "examples/extensions/bounded_leaves/pyproject.toml": "8731c3fa03f03341fcc987b449c04658c5bde3e7a4d24772e3bc2199ceb55eac", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "d53927c63c71ea3cf37083e0d71d11aff58941dbed1df430838c2d6b2912efff", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "41339c49f146181811dd0680d2b1abc563448ce35b4a79e8d4f0559c5d949baf" + }, + "run_id": "20260905T181351Z-1aee9568", + "modal_image_id": "im-5t2tgVcNHJsjGxYTfMVI8u" +} diff --git a/benchmarks/results/foundation/20260905T181351Z-1aee9568/results.json b/benchmarks/results/foundation/20260905T181351Z-1aee9568/results.json new file mode 100644 index 0000000..112e67c --- /dev/null +++ b/benchmarks/results/foundation/20260905T181351Z-1aee9568/results.json @@ -0,0 +1,267 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784222208, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "cuda-pathfinder": "1.4.0", + "joblib": "1.5.3", + "cuda-bindings": "13.1.1", + "pluggy": "1.6.0", + "openboost-example-bounded-leaves": "0.2.0", + "numba-cuda": "0.27.0", + "pip": "23.3.2", + "cuda-core": "0.6.0", + "numpy": "2.3.5", + "fastrlock": "0.8.3", + "iniconfig": "2.3.0", + "cupy-cuda12x": "13.6.0", + "pytest-xdist": "3.8.0", + "scipy": "1.16.3", + "openboost-example-normal-fisher": "0.2.0", + "packaging": "25.0", + "pytest": "9.0.2", + "openboost": "1.0.0rc1", + "setuptools": "69.0.3", + "pytest-cov": "7.0.0", + "coverage": "7.13.1", + "llvmlite": "0.46.0", + "execnet": "2.1.2", + "Pygments": "2.19.2", + "numba": "0.63.1", + "frozenlist": "1.6.0", + "aiosignal": "1.3.2", + "grpclib": "0.4.8", + "certifi": "2025.4.26", + "hyperframe": "6.1.0", + "yarl": "1.20.0", + "h2": "4.2.0", + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7", + "multidict": "6.4.4", + "attrs": "25.3.0", + "idna": "3.10", + "hpack": "4.1.0", + "propcache": "0.3.1", + "typing_extensions": "4.13.2", + "cbor2": "5.7.0", + "protobuf": "6.31.1" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "43fcda31b70a5970b0e588c1d0e4b836707af90e", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_extensions.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_extensions.py::test_installed_gpu_extensions\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n3 passed, 18 warnings in 36.81s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "installed_gpu_extensions": { + "installed": { + "normal_fisher": { + "version": "0.2.0", + "verified_python_files": 1, + "path": "lib/python3.12/site-packages/normal_fisher/__init__.py" + }, + "bounded_leaves": { + "version": "0.2.0", + "verified_python_files": 1, + "path": "lib/python3.12/site-packages/bounded_leaves/__init__.py" + } + }, + "math_oracle": true, + "cases": [ + { + "samples": 16, + "seed": 149, + "bounded": false, + "scheduled": false, + "data_sha256": "0a38db056ca01c92149477a5a8f688f076e8c0d8d638015b216f8afc2e516dc7", + "cpu": { + "nll": 1.3717080493544251, + "crps": 0.5507537146281264 + }, + "cuda": { + "nll": 1.3717080493544251, + "crps": 0.5507537146281264 + }, + "max_raw_error": 0.0 + }, + { + "samples": 16, + "seed": 149, + "bounded": false, + "scheduled": true, + "data_sha256": "0a38db056ca01c92149477a5a8f688f076e8c0d8d638015b216f8afc2e516dc7", + "cpu": { + "nll": 1.4621989866445437, + "crps": 0.5972907857652278 + }, + "cuda": { + "nll": 1.4621989866445437, + "crps": 0.5972907857652278 + }, + "max_raw_error": 0.0 + }, + { + "samples": 16, + "seed": 149, + "bounded": true, + "scheduled": false, + "data_sha256": "0a38db056ca01c92149477a5a8f688f076e8c0d8d638015b216f8afc2e516dc7", + "cpu": { + "nll": 1.5979890150606888, + "crps": 0.7010590233435069 + }, + "cuda": { + "nll": 1.5979890150606888, + "crps": 0.7010590233435069 + }, + "max_raw_error": 0.0 + }, + { + "samples": 16, + "seed": 149, + "bounded": true, + "scheduled": true, + "data_sha256": "0a38db056ca01c92149477a5a8f688f076e8c0d8d638015b216f8afc2e516dc7", + "cpu": { + "nll": 1.6266265375911129, + "crps": 0.7123083804741507 + }, + "cuda": { + "nll": 1.6266265375911129, + "crps": 0.7123083804741507 + }, + "max_raw_error": 0.0 + }, + { + "samples": 4097, + "seed": 149, + "bounded": false, + "scheduled": false, + "data_sha256": "089315b1628d919a064265a205ed8b561d1f682cbf41a999ed05196a101d0506", + "cpu": { + "nll": 1.320184317444835, + "crps": 0.50038932282263 + }, + "cuda": { + "nll": 1.320184334743105, + "crps": 0.5003893514229821 + }, + "max_raw_error": 3.5762786865234375e-07 + }, + { + "samples": 4097, + "seed": 149, + "bounded": false, + "scheduled": true, + "data_sha256": "089315b1628d919a064265a205ed8b561d1f682cbf41a999ed05196a101d0506", + "cpu": { + "nll": 1.401633907681933, + "crps": 0.5448231793044455 + }, + "cuda": { + "nll": 1.4016339307663064, + "crps": 0.5448231889723119 + }, + "max_raw_error": 2.384185791015625e-07 + }, + { + "samples": 4097, + "seed": 149, + "bounded": true, + "scheduled": false, + "data_sha256": "089315b1628d919a064265a205ed8b561d1f682cbf41a999ed05196a101d0506", + "cpu": { + "nll": 1.5818446737151965, + "crps": 0.6768309753701605 + }, + "cuda": { + "nll": 1.5818446737151965, + "crps": 0.6768309753701605 + }, + "max_raw_error": 0.0 + }, + { + "samples": 4097, + "seed": 149, + "bounded": true, + "scheduled": true, + "data_sha256": "089315b1628d919a064265a205ed8b561d1f682cbf41a999ed05196a101d0506", + "cpu": { + "nll": 1.605608203366832, + "crps": 0.6850206918510358 + }, + "cuda": { + "nll": 1.605608203366832, + "crps": 0.6850206918510358 + }, + "max_raw_error": 0.0 + } + ], + "clipping_changes_next_gradient": true, + "schedule_changes_prediction": true, + "compact_transfer_calls": 160, + "compact_transfer_bytes": 4480, + "models_saved": 9, + "demo": { + "data_sha256": "30edad3a2fe4867d52eaf13dcf9742e241d4b4874a448bb4dc0ea617b75dfcf1", + "device": "cuda", + "samples": 64, + "seed": 7, + "coefficients": { + "mu": [ + 0.2, + 0.1 + ], + "log_sigma": [ + 0.1, + 0.05 + ] + }, + "finite_positive_scale": true + } + }, + "extension_uninstall": { + "uninstall_returncode": 0, + "inference_returncode": 0, + "inference_stdout": "{\"extensions_absent\": true, \"exact_cpu_roundtrips\": 9}\n", + "stderr": "Using Python 3.12.1 environment at: /usr/local\nUninstalled 2 packages in 97ms\n - openboost-example-bounded-leaves==0.2.0 (from file:///opt/foundation/openboost_example_bounded_leaves-0.2.0-py3-none-any.whl)\n - openboost-example-normal-fisher==0.2.0 (from file:///opt/foundation/openboost_example_normal_fisher-0.2.0-py3-none-any.whl)\n" + } + }, + "remote_function_wall_s": 43.291684313999994, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md new file mode 100644 index 0000000..7fdfabe --- /dev/null +++ b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md @@ -0,0 +1,85 @@ +# P7 resident value: quality passes, performance budget fails + +Source `eb6121868a0ddcc25d4aae7c108e331e0ff52f4b`, clean wheel-only T4 run. +3 tests passed / 0 skipped; all 12 cells completed without fallback. This means +complete evidence, **not** a passing performance gate. Housing seeds 0/1/2, +12,384 train / 4,128 validation / 4,128 test rows, eight features, 30 rounds, +depth 3, learning rate .05, 64 bins; exact data/config and environment in manifest. + +| Strategy | Seed 0 warm fit s | Seed 1 | Seed 2 | Cross-seed median s | +|---|---:|---:|---:|---:| +| Legacy CPU | 2.4404 | 2.4481 | 2.5764 | 2.4481 | +| Legacy CUDA | .1605 | .1478 | .1496 | .1496 | +| Experimental default CUDA | 2.0855 | 2.0787 | 2.0733 | 2.0787 | +| Independent Fisher + bound + schedule CUDA | 2.1148 | 2.0735 | 2.0679 | 2.0735 | + +Each warm cell is the median of three fits after a process-first fit, all in an +independent process with fresh Numba/CuPy cache directories. The default candidate +is **13.899x** legacy CUDA by the preregistered cross-seed median ratio, well above +the 1.2 budget. Per-seed ratios are 12.995, 14.063 and 13.863. Experimental CPU +prediction also costs about .73–.74 s versus .068–.070 s for legacy GPU prediction. +This is an end-to-end API comparison with different prediction execution devices. + +Process-first fit ranges: CPU 4.440–4.589 s, legacy CUDA 2.304–3.283 s, +experimental default 19.655–19.915 s, independent extension 20.649–21.012 s. +Imports/data loading/device context startup are excluded, driver cache was not +cleared. These are not machine-cold times. Same-run legacy timings are the +comparator; older P2 timings have a different cache policy. + +| Seed | Default candidate NLL | CRPS | Coverage90 | Independent A+B+C NLL | CRPS | Coverage90 | +|---|---:|---:|---:|---:|---:|---:| +| 0 | 1.094474 | .398591 | .967781 | 1.483626 | .599585 | .923934 | +| 1 | 1.113435 | .410067 | .963905 | 1.511263 | .618114 | .915698 | +| 2 | 1.096392 | .403820 | .964390 | 1.494126 | .606814 | .922481 | + +Default quality passes on every fit/seed, and same-run legacy agrees with frozen +P2. This is numerical agreement, not good calibration: nominal 90% coverage is +96.4–96.8%. A+B+C changes curvature, clips leaves at .5 and uses tau=1 decay; +it has substantially worse proper scores here. Its closer coverage alone is not +a quality win. No tuning, seed deletion or threshold changes occurred. Strict +CUDA eval/callbacks remain unsupported and have no matching performance row. + +## Profiling limitations and follow-up + +Separate fifth fits collected host cProfile, path calls, named transfer wrappers +and 5 ms sampled device-wide memory. Path counts verify 30 objective / 60 trees; +legacy records 300 copy_to_host calls, experimental 300 asnumpy calls per fit. +Nested/internal/scalar copies are not a complete transfer audit. GPU memory +peaks sampled after warmup can equal the initial context/allocator footprint: +zero delta **does not mean zero training memory**. + +The sampling thread appears in the returned cProfile attribution (memGetInfo and +thread lock time); candidate parent/child cumulative times are also inconsistent. +Do not infer a causal time breakdown or an optimization target from those time +percentages. Original profiles remain verbatim. Follow up with a separate host +profile without a concurrent memory sampler; keep these timed fits unchanged. +No CUDA kernel trace was captured, and sampled memory is not an exact peak. + +T4, driver 580.95.05, runtime 12090, Python 3.12.1, NumPy 2.3.5, CuPy 13.6.0, +Numba 0.63.1 / numba-cuda 0.27.0. CPU requested 2, RAM 8192 MiB, threads 2; +CPU model not exposed. Pytest 293.30 s, remote function 297.30 s. Function time +includes initialization, processes, JIT and profiling; it is not a billing record. +Main and both plugin wheel hashes match P6 exactly (full hashes in manifest). + +Reproduce from the source revision: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite value +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_value +``` + +Validate the retained evidence: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T183820Z-3c245f2d +``` + +Decision: retain the experimental API for research; do not replace the legacy GPU +path or claim speed/cost superiority. G3 technical extension capability holds; +G4 has a negative performance result plus profiling/peak-memory gaps; G5 is open. + + +[Isolated follow-up](../20260905T184856Z-dcd49569/README.md) subsequently passed +with unchanged quality and separate host/memory fits. It localizes most diagnostic +time to the tree/session boundary. The original timings and raw profiles above +remain unchanged. diff --git a/benchmarks/results/foundation/20260905T183820Z-3c245f2d/junit.xml b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/junit.xml new file mode 100644 index 0000000..6f041e1 --- /dev/null +++ b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T183820Z-3c245f2d/manifest.json b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/manifest.json new file mode 100644 index 0000000..1c7eb5f --- /dev/null +++ b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/manifest.json @@ -0,0 +1,119 @@ +{ + "schema_version": 1, + "suite": "value", + "test_files": [ + "test_smoke.py", + "test_value.py" + ], + "source_sha": "eb6121868a0ddcc25d4aae7c108e331e0ff52f4b", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "dataset.py": "cee302d02c1f82e54c19693fdbff8a7671955f816cccb6fc5902860414109e2a", + "baseline_worker.py": "3abb07c372d2fae3ffc8973ff1b1220d207302b9f04282514762eb907e363e88", + "housing.json": "d511bf1b61c6e515598a91cc468b9c860dcde60f9e1e7a3e2570e9d8e8c3bc5b", + "test_value.py": "39657f535abf7640d2291739eb30d92b71927690072f740a632d94af3f7173a2", + "value_worker.py": "7e60f9d5a8478885b29bd7e71b86a49b049de39394bdf1a420b9d87316e56293", + "value_protocol.py": "0bed67ee94d26f66c37b6818c05fdfe723676f3af061d117b75ba2f6832a238a", + "p2_baseline.json": "1d024133ecd1975b7854eb7601b3a3a571981613aae8dd785dc98181ace78818", + "cal_housing.tgz": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_value" + ], + "gpu": "T4", + "timeout_s": 1800, + "retries": 0, + "dataset": { + "name": "California Housing", + "source_url": "https://ndownloader.figshare.com/files/5976036", + "archive_sha256": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681", + "member": "CaliforniaHousing/cal_housing.data", + "features": [ + "MedInc", + "HouseAge", + "AveRooms", + "AveBedrms", + "Population", + "AveOccup", + "Latitude", + "Longitude" + ], + "shape": [ + 20640, + 8 + ], + "target_unit": "100,000 USD", + "arrays_sha256": "34fc72e53a6329a89a9ae792e92f37853cea98adfbc6feea28cc08dc06f68ca1", + "dtype": "little-endian float32", + "split": "numpy.default_rng(seed), permutation, 60/20/20", + "split_sha256": { + "0": [ + "7d07544c6c3fddd72d0cfc8d0aa0c19cf00250f1890420bdb10bd80efb0c9c04", + "5f6d9931d5a5239c7933da9bb62df2c631e79017658f185742db94f139be358a", + "877744733fe0213e716ca084333f39385c30654169f117f772c53a6af8bcff37" + ], + "1": [ + "99578d3ef329ef275fd41e3834df7dab763525f9374db203b6f748b783eed05a", + "a161a60cd85d1ec54cbfd590f6befdb58b1a9a695c635bfbb562b4064d265caf", + "039c4fc07f6596a3cfdf73cb55820de4baa13f93251ede4c215796f283d46256" + ], + "2": [ + "51ab6cbe0019b5bea9f9da8a81b3fafa874d8c992062f65ee8d1b09a3b7898a1", + "14509d20c7d87948ac6ea9060c476995583dd130af0ff928a9c24709e5ecf9d4", + "ff6426d360c75b637dd866cd73d9fd1b9fcf310a886dc05fbac0f80dcf256ec8" + ] + } + }, + "extension_wheels": { + "openboost_example_normal_fisher-0.2.0-py3-none-any.whl": "47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b", + "openboost_example_bounded_leaves-0.2.0-py3-none-any.whl": "dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3" + }, + "extension_sources": { + "examples/extensions/normal_fisher/README.md": "78a5142eaf92a81df9c5908861a1ce66e5ae8b03bf753e0b84a834c5f2a5d0eb", + "examples/extensions/normal_fisher/pyproject.toml": "ab70495fafa430dfb1377a873cbb821c9484c9008cea26575fa3c3fa2a91e40d", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "cabafae48d9ad251eea62230b1d38ba7407fbb113dec116b3e840c05e032afd3", + "examples/extensions/normal_fisher/tests/test_normal.py": "6dcb48f9434879b054fe9a9e8b6fca5f4ba01782d8c45ed4c566a8306ee867c3", + "examples/extensions/bounded_leaves/README.md": "749f68fafb9e8cb5911c16aa79ab7a82593599e5c196c20be3ec9923172144e9", + "examples/extensions/bounded_leaves/pyproject.toml": "8731c3fa03f03341fcc987b449c04658c5bde3e7a4d24772e3bc2199ceb55eac", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "d53927c63c71ea3cf37083e0d71d11aff58941dbed1df430838c2d6b2912efff", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "41339c49f146181811dd0680d2b1abc563448ce35b4a79e8d4f0559c5d949baf" + }, + "value_protocol": { + "seeds": [ + 0, + 1, + 2 + ], + "strategies": [ + "legacy_cpu", + "legacy_cuda", + "experimental_cuda", + "extensions_cuda" + ], + "mode": "resident", + "repetitions": 4, + "warm_fit_budget_ratio": 1.2, + "extension_bound": 0.5, + "extension_schedule_tau": 1, + "cache_policy": "fresh process and NUMBA/CUPY cache directories per cell; device context startup/imports excluded; driver cache not cleared", + "profile": "separate fifth warm fit, cProfile host attribution and 5 ms sampled device-wide memory; not a kernel trace", + "cost": "T4 seconds only; billing unavailable" + }, + "run_id": "20260905T183820Z-3c245f2d", + "modal_image_id": "im-hSRavxWM5HKjZyxDZNluhK" +} diff --git a/benchmarks/results/foundation/20260905T183820Z-3c245f2d/results.json b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/results.json new file mode 100644 index 0000000..d88e363 --- /dev/null +++ b/benchmarks/results/foundation/20260905T183820Z-3c245f2d/results.json @@ -0,0 +1,7184 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784173056, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "Pygments": "2.19.2", + "scipy": "1.16.3", + "execnet": "2.1.2", + "llvmlite": "0.46.0", + "pip": "23.3.2", + "joblib": "1.5.3", + "numba": "0.63.1", + "pytest-xdist": "3.8.0", + "cupy-cuda12x": "13.6.0", + "numpy": "2.3.5", + "coverage": "7.13.1", + "pytest-cov": "7.0.0", + "setuptools": "69.0.3", + "openboost-example-bounded-leaves": "0.2.0", + "pytest": "9.0.2", + "pluggy": "1.6.0", + "packaging": "25.0", + "openboost-example-normal-fisher": "0.2.0", + "iniconfig": "2.3.0", + "openboost": "1.0.0rc1", + "cuda-bindings": "13.1.1", + "cuda-core": "0.6.0", + "fastrlock": "0.8.3", + "numba-cuda": "0.27.0", + "cuda-pathfinder": "1.4.0", + "h2": "4.2.0", + "protobuf": "6.31.1", + "aiosignal": "1.3.2", + "multidict": "6.4.4", + "attrs": "25.3.0", + "hyperframe": "6.1.0", + "yarl": "1.20.0", + "typing_extensions": "4.13.2", + "aiohttp": "3.12.7", + "propcache": "0.3.1", + "certifi": "2025.4.26", + "frozenlist": "1.6.0", + "grpclib": "0.4.8", + "aiohappyeyeballs": "2.6.1", + "cbor2": "5.7.0", + "idna": "3.10", + "hpack": "4.1.0" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "eb6121868a0ddcc25d4aae7c108e331e0ff52f4b", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_value.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n3 passed, 17 warnings in 293.30s (0:04:53)\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "value_cells": [ + { + "strategy": "legacy_cpu", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 4.4399111819999995, + "predict_s": 0.6888748049999993, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + }, + { + "phase": "warm_1", + "fit_s": 2.4811614109999987, + "predict_s": 0.7115935009999994, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + }, + { + "phase": "warm_2", + "fit_s": 2.41866293, + "predict_s": 0.6892554349999997, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + }, + { + "phase": "warm_3", + "fit_s": 2.4404499119999983, + "predict_s": 0.707289277000001, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.42800819, + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.1142e-05, + "cumulative_s": 2.427887527 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.301e-05, + "cumulative_s": 2.427811799 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.004038703, + "cumulative_s": 2.427767475 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00033725300000000003, + "cumulative_s": 1.987833047 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 7.522400000000001e-05, + "cumulative_s": 1.9873908990000002 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000156386, + "cumulative_s": 1.987315675 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 1.9837559070000002, + "cumulative_s": 1.9870392440000002 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.001401804, + "cumulative_s": 0.394956527 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.006196979, + "cumulative_s": 0.39164817 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00095688, + "cumulative_s": 0.133144484 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.06375668100000001, + "cumulative_s": 0.131794574 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000573673, + "cumulative_s": 0.089979922 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.079300202, + "cumulative_s": 0.088742321 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.2665000000000006e-05, + "cumulative_s": 0.073282783 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00043552300000000005, + "cumulative_s": 0.073220196 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00644282, + "cumulative_s": 0.07027446000000001 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.005023058, + "cumulative_s": 0.059639163 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.054023559000000006, + "cumulative_s": 0.055514766 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.8626e-05, + "cumulative_s": 0.053504522000000006 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 9.1538e-05, + "cumulative_s": 0.053310386 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.041235127, + "cumulative_s": 0.050825839000000005 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.036e-05, + "cumulative_s": 0.046574307 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.8268e-05, + "cumulative_s": 0.046386522 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00026755800000000004, + "cumulative_s": 0.046314271000000004 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000125887, + "cumulative_s": 0.046089414 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.037535466, + "cumulative_s": 0.045834367 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.1133e-05, + "cumulative_s": 0.044418352 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.0473000000000002e-05, + "cumulative_s": 0.044093305 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000175059, + "cumulative_s": 0.043966839 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000346017, + "cumulative_s": 0.040150999 + }, + { + "file": "_distributions.py", + "line": 211, + "function": "natural_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0049517860000000006, + "cumulative_s": 0.037775678 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.54e-06, + "cumulative_s": 0.03761078 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.332000000000001e-06, + "cumulative_s": 0.037603094000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00029544900000000004, + "cumulative_s": 0.037589259 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.002305194, + "cumulative_s": 0.030383999000000002 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 186, + "calls": 196, + "self_s": 0.000905699, + "cumulative_s": 0.030047022000000003 + }, + { + "file": "_arraysetops_impl.py", + "line": 348, + "function": "_unique1d", + "primitive_calls": 186, + "calls": 196, + "self_s": 0.0022466590000000002, + "cumulative_s": 0.029020377000000003 + }, + { + "file": "_distributions.py", + "line": 393, + "function": "nll_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0026824260000000003, + "cumulative_s": 0.027894367000000003 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.1096e-05, + "cumulative_s": 0.026407003000000002 + }, + { + "file": "numeric.py", + "line": 324, + "function": "full", + "primitive_calls": 1022, + "calls": 1022, + "self_s": 0.004249346, + "cumulative_s": 0.026173685000000002 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000346017, + "cumulative_s": 0.040150999 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000233904, + "cumulative_s": 0.000364407 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.4404499119999983, + "warm_predict_median_s": 0.707289277000001 + }, + { + "strategy": "legacy_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 3.2827203760000003, + "predict_s": 0.19982650899999754, + "metrics": { + "nll": 1.094473763675832, + "crps": 0.3985909541783186, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "17b796e908815fd2564a3749ae0e1708c29c81f13df2d87066a63ad39df8bd67", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 0.16122258600000094, + "predict_s": 0.07122610900000126, + "metrics": { + "nll": 1.0944737656515569, + "crps": 0.3985909542460625, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "66cdd000f091f267f571ebf9d8db518306d271c2e3abf5fa69c94f52bce847be", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 0.15966573400000073, + "predict_s": 0.06971644400000088, + "metrics": { + "nll": 1.0944737657950687, + "crps": 0.39859095572575465, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "cab40076cdf5057a185c291bbc61ec88b9c50c3b88668b7d875cee45b91e1e65", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 0.16048371200000133, + "predict_s": 0.06818093400000436, + "metrics": { + "nll": 1.0944737626717287, + "crps": 0.39859095366532676, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "f61d9cdcbd55ce0cd479acc3591e72e3730f7e7ab5920d4d6a94f3ce9457e217", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.3091158849999971, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 50, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 9.436600000000001e-05, + "cumulative_s": 0.30833311500000005 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000122384, + "cumulative_s": 0.30830627600000005 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.001296044, + "cumulative_s": 0.307862959 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0006866810000000001, + "cumulative_s": 0.22481877800000002 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.004122946000000001, + "cumulative_s": 0.209365824 + }, + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.00125877, + "cumulative_s": 0.20316482400000002 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.003733418, + "cumulative_s": 0.20183667000000002 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.030641815000000003, + "cumulative_s": 0.18202116200000001 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.033151097000000004, + "cumulative_s": 0.11953165 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.7758e-05, + "cumulative_s": 0.073082008 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00034336100000000004, + "cumulative_s": 0.07302523000000001 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.002010219, + "cumulative_s": 0.070567102 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.024170799000000003, + "cumulative_s": 0.055308539000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.7693e-05, + "cumulative_s": 0.052840985 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.7321e-05, + "cumulative_s": 0.052682537 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4220, + "calls": 4220, + "self_s": 0.017137747000000002, + "cumulative_s": 0.045916868 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.572e-05, + "cumulative_s": 0.045907729 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.0869000000000003e-05, + "cumulative_s": 0.045745810000000005 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000239575, + "cumulative_s": 0.045697358 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.061e-05, + "cumulative_s": 0.044509875000000004 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.1557e-05, + "cumulative_s": 0.044253292 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00018950500000000002, + "cumulative_s": 0.044075182000000004 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2151, + "calls": 2151, + "self_s": 0.0024423030000000003, + "cumulative_s": 0.039269226000000004 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 4.7880000000000006e-06, + "cumulative_s": 0.03766893 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00010704800000000001, + "cumulative_s": 0.03765939 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000185071, + "cumulative_s": 0.037414042 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.00189769, + "cumulative_s": 0.036453981 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 1535, + "calls": 1535, + "self_s": 0.000841151, + "cumulative_s": 0.034354153000000005 + }, + { + "file": "devices.py", + "line": 147, + "function": "_get_or_create_context_uncached", + "primitive_calls": 1835, + "calls": 1835, + "self_s": 0.009628812, + "cumulative_s": 0.034122264 + }, + { + "file": "devicearray.py", + "line": 66, + "function": "__init__", + "primitive_calls": 316, + "calls": 316, + "self_s": 0.0015123270000000002, + "cumulative_s": 0.028288872000000003 + }, + { + "file": "driver.py", + "line": 1206, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.00024148300000000002, + "cumulative_s": 0.026384944 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000183811, + "cumulative_s": 0.026105333 + }, + { + "file": "driver.py", + "line": 929, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.0007748420000000001, + "cumulative_s": 0.025976881 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.0453e-05, + "cumulative_s": 0.025659372000000003 + }, + { + "file": "driver.py", + "line": 466, + "function": "__enter__", + "primitive_calls": 2451, + "calls": 2451, + "self_s": 0.009910210000000001, + "cumulative_s": 0.025521499000000003 + }, + { + "file": "_objectives.py", + "line": 161, + "function": "_step_device", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000214343, + "cumulative_s": 0.025501675 + }, + { + "file": "_objectives.py", + "line": 117, + "function": "init_raw", + "primitive_calls": 1, + "calls": 1, + "self_s": 1.896e-05, + "cumulative_s": 0.02531966 + }, + { + "file": "_distributions.py", + "line": 384, + "function": "init_params", + "primitive_calls": 1, + "calls": 1, + "self_s": 5.529e-05, + "cumulative_s": 0.025297342 + }, + { + "file": "_cuda.py", + "line": 3686, + "function": "normal_step_gpu", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000297953, + "cumulative_s": 0.025234842 + }, + { + "file": "fromnumeric.py", + "line": 3869, + "function": "std", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.6974e-05, + "cumulative_s": 0.024974533 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0006866810000000001, + "cumulative_s": 0.22481877800000002 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000183811, + "cumulative_s": 0.026105333 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00200319, + "cumulative_s": 0.010884590000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 0.16048371200000133, + "warm_predict_median_s": 0.06971644400000088 + }, + { + "strategy": "experimental_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 19.655296782999997, + "predict_s": 0.7374716509999999, + "metrics": { + "nll": 1.0944737276720284, + "crps": 0.3985909440140499, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "48981163da33e4fb7192fe61ac5ed6ebb7b972cc2336b5cd6deca4a72071d255", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 2.103727072000005, + "predict_s": 0.7311044340000024, + "metrics": { + "nll": 1.0944737123931025, + "crps": 0.3985909341743707, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "8630bad37dd590fbdb617328946d1f32a95c89a730dd6e203c9c7dd5deaabfa8", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.0672303549999995, + "predict_s": 0.7334089479999975, + "metrics": { + "nll": 1.0944737164589813, + "crps": 0.39859093631735687, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "1187fdd18f4ace5f3dc637509f97edf7ff44bab460b3f49f585b6a82cd7179b2", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 2.085456484000005, + "predict_s": 0.7501454340000038, + "metrics": { + "nll": 1.094473756745003, + "crps": 0.3985909615154505, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "60a78fae167f7e13062d8d60896529705f0e8496451c3336f98497002d3fd940", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.668827239999999, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 334430208, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 0, + "samples": 405, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1659, + "calls": 1801, + "self_s": 0.723235348, + "cumulative_s": 1.6245195840000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 403, + "calls": 403, + "self_s": 0.292288161, + "cumulative_s": 0.292288161 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.08148016400000001, + "cumulative_s": 0.28732240200000003 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.139300209, + "cumulative_s": 0.20175198700000002 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09442303, + "cumulative_s": 0.195825928 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09239594300000001, + "cumulative_s": 0.181553147 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.12356977400000001, + "cumulative_s": 0.16143501200000002 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.004130334, + "cumulative_s": 0.14580035200000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.138695762, + "cumulative_s": 0.138695762 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.023958328, + "cumulative_s": 0.128394471 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.126565544, + "cumulative_s": 0.126565544 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.003124719, + "cumulative_s": 0.105793814 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006577595, + "cumulative_s": 0.08064589400000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.8557000000000004e-05, + "cumulative_s": 0.07127283000000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.0006132290000000001, + "cumulative_s": 0.07121530100000001 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009758334, + "cumulative_s": 0.061259471 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029893706000000003, + "cumulative_s": 0.060878456000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.8802000000000004e-05, + "cumulative_s": 0.052217925000000005 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.8756e-05, + "cumulative_s": 0.052050132000000006 + }, + { + "file": "basic.py", + "line": 12, + "function": "copyto", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.027708084, + "cumulative_s": 0.051465351000000006 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000330129, + "cumulative_s": 0.04532891 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.6326000000000002e-05, + "cumulative_s": 0.044914821 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 7.8731e-05, + "cumulative_s": 0.044780593 + }, + { + "file": "from_data.py", + "line": 56, + "function": "asarray", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.00041560900000000005, + "cumulative_s": 0.044610212 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00021359200000000002, + "cumulative_s": 0.044558856 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.02122591, + "cumulative_s": 0.044168879 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.043701099, + "cumulative_s": 0.043701099 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.4773e-05, + "cumulative_s": 0.043451039000000004 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.3951e-05, + "cumulative_s": 0.043167042 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00017518400000000002, + "cumulative_s": 0.042917557 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000208912, + "cumulative_s": 0.041761428 + }, + { + "file": "_objectives.py", + "line": 161, + "function": "_step_device", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000286168, + "cumulative_s": 0.041214935 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.015013643, + "cumulative_s": 0.04106125 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.001643509, + "cumulative_s": 0.040918341000000004 + }, + { + "file": "_cuda.py", + "line": 3686, + "function": "normal_step_gpu", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00048804900000000006, + "cumulative_s": 0.040861134 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018799306000000002, + "cumulative_s": 0.038001299 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.020526683, + "cumulative_s": 0.037056297 + }, + { + "file": "_batch_split.py", + "line": 165, + "function": "", + "primitive_calls": 900, + "calls": 900, + "self_s": 0.000926258, + "cumulative_s": 0.030349303 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 2.7028000000000003e-05, + "cumulative_s": 0.025627378000000003 + }, + { + "file": "_device.py", + "line": 102, + "function": "", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.005989355, + "cumulative_s": 0.024395555000000003 + } + ], + "path_functions": [ + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.08148016400000001, + "cumulative_s": 0.28732240200000003 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.12356977400000001, + "cumulative_s": 0.16143501200000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006577595, + "cumulative_s": 0.08064589400000001 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000208912, + "cumulative_s": 0.041761428 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00032755400000000003, + "cumulative_s": 0.007596085000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.085456484000005, + "warm_predict_median_s": 0.7334089479999975 + }, + { + "strategy": "extensions_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 21.011644735999994, + "predict_s": 0.7334201550000046, + "metrics": { + "nll": 1.483626390733397, + "crps": 0.5995845072849281, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "278185270b2428de6c0f839223e2f127788ca6c723b9284b8b92615492b12ed7", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 2.114789467999998, + "predict_s": 0.7304544050000032, + "metrics": { + "nll": 1.4836263907987848, + "crps": 0.5995845072931998, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "650bc92dc0275d34d3db6af89d982fc989b02c0390cc5f13d7efcef662f58bfa", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.135097118999994, + "predict_s": 0.7335890909999989, + "metrics": { + "nll": 1.4836263968752028, + "crps": 0.5995845157585404, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "10493add9fdead7f093b6a9b3b3d10b7a86f98f550e217b97b77b1603c1b8736", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 2.0765438800000027, + "predict_s": 0.7299117139999964, + "metrics": { + "nll": 1.483626398086066, + "crps": 0.5995845154583398, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "402656d7982b9460ff5c745fc2430348aedd07ca1d349d82639a1eb2bbde6a10", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.6027335960000073, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 401, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 2044928 + }, + "top_host_functions": [ + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1631, + "calls": 1785, + "self_s": 0.7251451800000001, + "cumulative_s": 1.682474531 + }, + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 0.000523924, + "cumulative_s": 0.900367196 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.09478391700000001, + "cumulative_s": 0.306203188 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 399, + "calls": 399, + "self_s": 0.25887536, + "cumulative_s": 0.25887536 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.138058899, + "cumulative_s": 0.20063054100000002 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.148183847, + "cumulative_s": 0.18863977 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09023753200000001, + "cumulative_s": 0.188397948 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09144075700000001, + "cumulative_s": 0.181604186 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.003979627, + "cumulative_s": 0.160618069 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.142215225, + "cumulative_s": 0.15543767 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.137398446, + "cumulative_s": 0.14384799 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.024494652000000002, + "cumulative_s": 0.142212197 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.0027950680000000004, + "cumulative_s": 0.10861100400000001 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006602799, + "cumulative_s": 0.103129012 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.9002e-05, + "cumulative_s": 0.06959023 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000490376, + "cumulative_s": 0.069519391 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.013747643, + "cumulative_s": 0.056515659 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.027907097000000002, + "cumulative_s": 0.056246158000000004 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004317258, + "cumulative_s": 0.055604938000000007 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.001757185, + "cumulative_s": 0.054710480000000006 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.6897e-05, + "cumulative_s": 0.044161546 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.1186e-05, + "cumulative_s": 0.043981412000000004 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000203618, + "cumulative_s": 0.043930004 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.042650749, + "cumulative_s": 0.042650749 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.0723000000000004e-05, + "cumulative_s": 0.042220602 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.0119e-05, + "cumulative_s": 0.041956724 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000141433, + "cumulative_s": 0.041815086 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000467461, + "cumulative_s": 0.040071548000000005 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.007988006, + "cumulative_s": 0.038480162000000005 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009510325, + "cumulative_s": 0.038267549000000005 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.010034469, + "cumulative_s": 0.03815023 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.948e-06, + "cumulative_s": 0.037103541000000004 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000164282, + "cumulative_s": 0.037087924 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000168951, + "cumulative_s": 0.036641356 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.017968772, + "cumulative_s": 0.036306914 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.019645773000000002, + "cumulative_s": 0.035663918 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.8909e-05, + "cumulative_s": 0.035112758 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 9.0252e-05, + "cumulative_s": 0.034978437 + }, + { + "file": "_batch_split.py", + "line": 165, + "function": "", + "primitive_calls": 900, + "calls": 900, + "self_s": 0.000835048, + "cumulative_s": 0.029595703 + }, + { + "file": "basic.py", + "line": 12, + "function": "copyto", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.026844865000000002, + "cumulative_s": 0.028722285 + } + ], + "path_functions": [ + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.09478391700000001, + "cumulative_s": 0.306203188 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.148183847, + "cumulative_s": 0.18863977 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006602799, + "cumulative_s": 0.103129012 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00029369600000000003, + "cumulative_s": 0.0071012110000000005 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.114789467999998, + "warm_predict_median_s": 0.7304544050000032 + }, + { + "strategy": "legacy_cpu", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 4.536489328000002, + "predict_s": 0.6979556920000078, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + }, + { + "phase": "warm_1", + "fit_s": 2.460763015000012, + "predict_s": 0.6997583010000028, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + }, + { + "phase": "warm_2", + "fit_s": 2.444382576999999, + "predict_s": 0.7027744499999926, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + }, + { + "phase": "warm_3", + "fit_s": 2.4481368569999944, + "predict_s": 0.7030640029999944, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.4288418220000096, + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.2036e-05, + "cumulative_s": 2.428719428 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 5.4905000000000004e-05, + "cumulative_s": 2.428633163 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.004178068, + "cumulative_s": 2.4285772480000003 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00033116600000000003, + "cumulative_s": 2.0115402980000003 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 8.2964e-05, + "cumulative_s": 2.0111037080000003 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000163749, + "cumulative_s": 2.011020744 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 2.0071803100000003, + "cumulative_s": 2.010738422 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00149565, + "cumulative_s": 0.37482175100000004 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.006295827, + "cumulative_s": 0.371086584 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000782824, + "cumulative_s": 0.12149810000000001 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.062883034, + "cumulative_s": 0.12030112000000001 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000507425, + "cumulative_s": 0.08851407 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.078650288, + "cumulative_s": 0.087350484 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 2.7556000000000002e-05, + "cumulative_s": 0.066156347 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00028404600000000004, + "cumulative_s": 0.066114264 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.005399251000000001, + "cumulative_s": 0.061191902000000006 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 3831, + "calls": 3839, + "self_s": 0.018484935, + "cumulative_s": 0.052313078000000006 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.0045254990000000005, + "cumulative_s": 0.051734564000000004 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00010858700000000001, + "cumulative_s": 0.04702085 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.038324368000000004, + "cumulative_s": 0.046788420000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.0390000000000004e-05, + "cumulative_s": 0.046357961 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.7512e-05, + "cumulative_s": 0.046238485 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.044082316, + "cumulative_s": 0.045439013 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.034945226, + "cumulative_s": 0.04370074 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.6008e-05, + "cumulative_s": 0.040962218 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.5886e-05, + "cumulative_s": 0.040842083 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000169597, + "cumulative_s": 0.040799442000000005 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.214e-05, + "cumulative_s": 0.039969121 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.343e-05, + "cumulative_s": 0.039789684000000006 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00012628500000000001, + "cumulative_s": 0.039730883 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00040388, + "cumulative_s": 0.037136015 + }, + { + "file": "fromnumeric.py", + "line": 2241, + "function": "clip", + "primitive_calls": 36, + "calls": 38, + "self_s": 0.000240394, + "cumulative_s": 0.035318471000000004 + }, + { + "file": "fromnumeric.py", + "line": 51, + "function": "_wrapfunc", + "primitive_calls": 452, + "calls": 466, + "self_s": 0.0009191080000000001, + "cumulative_s": 0.032242872 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.637e-06, + "cumulative_s": 0.031535939 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.482e-06, + "cumulative_s": 0.031530178 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000185365, + "cumulative_s": 0.031519242 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.002354086, + "cumulative_s": 0.031093730000000003 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 187, + "calls": 196, + "self_s": 0.0009328520000000001, + "cumulative_s": 0.030207457000000004 + }, + { + "file": "_arraysetops_impl.py", + "line": 348, + "function": "_unique1d", + "primitive_calls": 187, + "calls": 196, + "self_s": 0.0021635300000000003, + "cumulative_s": 0.028008254000000003 + }, + { + "file": "numeric.py", + "line": 324, + "function": "full", + "primitive_calls": 1022, + "calls": 1022, + "self_s": 0.003989907, + "cumulative_s": 0.023775277 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00040388, + "cumulative_s": 0.037136015 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000247543, + "cumulative_s": 0.00039866600000000005 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.4481368569999944, + "warm_predict_median_s": 0.7027744499999926 + }, + { + "strategy": "legacy_cuda", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 2.303823792000003, + "predict_s": 0.15968889699999522, + "metrics": { + "nll": 1.11343459752752, + "crps": 0.41006695090815726, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "808547b21cbdbba24594978122f1c90896b992e768668d5b7a1f951058a5f3fc", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 0.14780975299999, + "predict_s": 0.06720372499998462, + "metrics": { + "nll": 1.1134345840403186, + "crps": 0.41006694252041553, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "ad2ad19c8ed787cf5a3c0726c2428c334831cd6f906ec51fee459a03de56dc7a", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 0.14839307300002247, + "predict_s": 0.06756039200001851, + "metrics": { + "nll": 1.1134345802327983, + "crps": 0.4100669411901907, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "4f8b186ed332f30898ef850565ece812302b3b89b2d89a84965f2785ed427372", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 0.14777464499999837, + "predict_s": 0.07119548599999348, + "metrics": { + "nll": 1.1134345948741042, + "crps": 0.4100669514275967, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "111a61525c01b8e18b686fdd5d863611c11b98ee866cd922525cf9365d823b0f", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.342254386999997, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 52, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 8.8069e-05, + "cumulative_s": 0.34134393 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.16e-05, + "cumulative_s": 0.298179437 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.0011482130000000001, + "cumulative_s": 0.297940388 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0006943, + "cumulative_s": 0.24250588 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.004053321, + "cumulative_s": 0.22511018800000002 + }, + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.001206693, + "cumulative_s": 0.21153560000000002 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.003692737, + "cumulative_s": 0.21027193400000002 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.030688531, + "cumulative_s": 0.19038180400000002 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.033781136, + "cumulative_s": 0.12309845100000001 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.001981545, + "cumulative_s": 0.110132831 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.4298e-05, + "cumulative_s": 0.085897448 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000358828, + "cumulative_s": 0.08583045 + }, + { + "file": "_array.py", + "line": 509, + "function": "as_numba_array", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000705207, + "cumulative_s": 0.059411144000000006 + }, + { + "file": "_cuda.py", + "line": 1696, + "function": "as_cuda_array", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00030991, + "cumulative_s": 0.057055598000000006 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.024445193, + "cumulative_s": 0.057013069000000006 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4220, + "calls": 4220, + "self_s": 0.017403511, + "cumulative_s": 0.055568953000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.0006e-05, + "cumulative_s": 0.053213178 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 9.3502e-05, + "cumulative_s": 0.053076099 + }, + { + "file": "devicearray.py", + "line": 66, + "function": "__init__", + "primitive_calls": 316, + "calls": 316, + "self_s": 0.001581619, + "cumulative_s": 0.052414754 + }, + { + "file": "api.py", + "line": 116, + "function": "to_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 5.7798000000000005e-05, + "cumulative_s": 0.047816544 + }, + { + "file": "devicearray.py", + "line": 910, + "function": "auto_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 8.2107e-05, + "cumulative_s": 0.047281637 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.7209000000000003e-05, + "cumulative_s": 0.045931846000000005 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.0414e-05, + "cumulative_s": 0.045650862 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000264065, + "cumulative_s": 0.045566802000000003 + }, + { + "file": "devicearray.py", + "line": 850, + "function": "from_array_like", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.8171000000000003e-05, + "cumulative_s": 0.044659802000000005 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.2074000000000002e-05, + "cumulative_s": 0.044260427000000005 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.7301000000000003e-05, + "cumulative_s": 0.044178604 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000250123, + "cumulative_s": 0.044144975 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2151, + "calls": 2151, + "self_s": 0.002468491, + "cumulative_s": 0.042291844 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.001858735, + "cumulative_s": 0.039265553 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 1535, + "calls": 1535, + "self_s": 0.0008318090000000001, + "cumulative_s": 0.037240427 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.648e-06, + "cumulative_s": 0.036978204 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 6.8790000000000005e-06, + "cumulative_s": 0.036972265000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00029357200000000003, + "cumulative_s": 0.036958744 + }, + { + "file": "driver.py", + "line": 466, + "function": "__enter__", + "primitive_calls": 2451, + "calls": 2451, + "self_s": 0.009919164000000001, + "cumulative_s": 0.035764673000000004 + }, + { + "file": "devices.py", + "line": 147, + "function": "_get_or_create_context_uncached", + "primitive_calls": 1835, + "calls": 1835, + "self_s": 0.009522642000000001, + "cumulative_s": 0.033699070000000005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1521, + "calls": 1534, + "self_s": 0.001152458, + "cumulative_s": 0.031783304000000005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 50, + "calls": 50, + "self_s": 0.031511509, + "cumulative_s": 0.031511509 + }, + { + "file": "contextlib.py", + "line": 132, + "function": "__enter__", + "primitive_calls": 617, + "calls": 617, + "self_s": 0.0005306590000000001, + "cumulative_s": 0.029392727 + }, + { + "file": "devices.py", + "line": 111, + "function": "ensure_context", + "primitive_calls": 1232, + "calls": 1232, + "self_s": 0.0025533400000000003, + "cumulative_s": 0.028115026 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0006943, + "cumulative_s": 0.24250588 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00016652300000000002, + "cumulative_s": 0.026727191 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00199951, + "cumulative_s": 0.011059735000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 0.14780975299999, + "warm_predict_median_s": 0.06756039200001851 + }, + { + "strategy": "experimental_cuda", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 19.914643098, + "predict_s": 0.7387277069999811, + "metrics": { + "nll": 1.113434541202327, + "crps": 0.41006692747368073, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8af6aba99e9c9e51367dbdfb1dbe8441554fc78c51dd9cbbf57534943ec6eeca", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 2.0765768969999954, + "predict_s": 0.7392912279999848, + "metrics": { + "nll": 1.113434547935153, + "crps": 0.4100669350107812, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "2546f03bafe0590e397a56f6fd19119c934e32309766b304b84f908ee310f726", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.0786940920000063, + "predict_s": 0.7374507860000108, + "metrics": { + "nll": 1.1134345235950027, + "crps": 0.4100669260848533, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "033442ecfd0d19dc26893ed623f7c521c3e5d3fdaa03e94a456bd7125d6315c7", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 2.1098252230000014, + "predict_s": 0.7397164869999813, + "metrics": { + "nll": 1.113434548602799, + "crps": 0.41006693303228514, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "2cf9a49c589af21f9a7c5fc9b25a753137e92f3bd30ef39560c69fdbc20af031", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.588399796999994, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 334430208, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 0, + "samples": 399, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1623, + "calls": 1777, + "self_s": 0.73393164, + "cumulative_s": 1.6189590680000001 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.077538223, + "cumulative_s": 0.269629914 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 397, + "calls": 397, + "self_s": 0.23799303000000002, + "cumulative_s": 0.23799303000000002 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.138929719, + "cumulative_s": 0.20336501 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.153471349, + "cumulative_s": 0.19651268800000002 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.092494562, + "cumulative_s": 0.19150645400000002 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09203897500000001, + "cumulative_s": 0.182928676 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.003909513, + "cumulative_s": 0.158324724 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.138241036, + "cumulative_s": 0.15149110500000001 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0244585, + "cumulative_s": 0.12727064300000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.12555891400000002, + "cumulative_s": 0.12555891400000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006764036, + "cumulative_s": 0.109836373 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002915509, + "cumulative_s": 0.104860616 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009421543000000001, + "cumulative_s": 0.08564589900000001 + }, + { + "file": "basic.py", + "line": 12, + "function": "copyto", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.027197488000000002, + "cumulative_s": 0.076194553 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000329794, + "cumulative_s": 0.063672534 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029275110000000003, + "cumulative_s": 0.059836940000000005 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.6951000000000004e-05, + "cumulative_s": 0.055489276000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000378378, + "cumulative_s": 0.055407169000000006 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.646700000000001e-05, + "cumulative_s": 0.052875185000000005 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000343259, + "cumulative_s": 0.052693210000000004 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.1916000000000004e-05, + "cumulative_s": 0.045498796 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.3124e-05, + "cumulative_s": 0.045350276 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000203734, + "cumulative_s": 0.045278028000000005 + }, + { + "file": "from_data.py", + "line": 56, + "function": "asarray", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.000396876, + "cumulative_s": 0.044492629000000006 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.044295307000000006, + "cumulative_s": 0.044295307000000006 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.020100205000000003, + "cumulative_s": 0.044082429 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.1698e-05, + "cumulative_s": 0.043676077 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.5388e-05, + "cumulative_s": 0.04335312 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000222818, + "cumulative_s": 0.042356093000000004 + }, + { + "file": "_objectives.py", + "line": 161, + "function": "_step_device", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000290751, + "cumulative_s": 0.04178374 + }, + { + "file": "_cuda.py", + "line": 3686, + "function": "normal_step_gpu", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000430281, + "cumulative_s": 0.041422475 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.001690487, + "cumulative_s": 0.04094155 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.014869043, + "cumulative_s": 0.040643515000000005 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 120, + "calls": 120, + "self_s": 0.000562972, + "cumulative_s": 0.040279185 + }, + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 0.000249937, + "cumulative_s": 0.039838022 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 9.722000000000001e-06, + "cumulative_s": 0.038101546 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000130719, + "cumulative_s": 0.03808185 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00015719800000000002, + "cumulative_s": 0.037742812 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018599909, + "cumulative_s": 0.037548101 + } + ], + "path_functions": [ + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.077538223, + "cumulative_s": 0.269629914 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.153471349, + "cumulative_s": 0.19651268800000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006764036, + "cumulative_s": 0.109836373 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000222818, + "cumulative_s": 0.042356093000000004 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000307018, + "cumulative_s": 0.007215535 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.0786940920000063, + "warm_predict_median_s": 0.7392912279999848 + }, + { + "strategy": "extensions_cuda", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 20.742681612000013, + "predict_s": 0.7477197169999954, + "metrics": { + "nll": 1.5112631581026819, + "crps": 0.6181142100151151, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "faa24c7c7ee09a687e5892ddd553824dccbfbf5b90fd0d0ea1741fd681a182da", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 2.0983434569999986, + "predict_s": 0.7356921729999897, + "metrics": { + "nll": 1.5112631540498538, + "crps": 0.6181142057713688, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "383628ea6384beaaf0a40926fcf587c5ef521769dbf24ae9e314e1b86c04afe4", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.0673054579999928, + "predict_s": 0.7398684059999994, + "metrics": { + "nll": 1.5112631538721262, + "crps": 0.6181142057220822, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "383628ea6384beaaf0a40926fcf587c5ef521769dbf24ae9e314e1b86c04afe4", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 2.073455617999997, + "predict_s": 0.7381084390000012, + "metrics": { + "nll": 1.51126315828041, + "crps": 0.6181142100644017, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "faa24c7c7ee09a687e5892ddd553824dccbfbf5b90fd0d0ea1741fd681a182da", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.5515522369999815, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 391, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1922048 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 0.001790272, + "cumulative_s": 2.550311379 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1591, + "calls": 1745, + "self_s": 0.7178039580000001, + "cumulative_s": 1.592167641 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.092276757, + "cumulative_s": 0.31229900600000005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 389, + "calls": 389, + "self_s": 0.23754191300000002, + "cumulative_s": 0.23754191300000002 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.157873689, + "cumulative_s": 0.19941302700000002 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.13420531400000002, + "cumulative_s": 0.19541833600000003 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.092212712, + "cumulative_s": 0.190066391 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.08856287, + "cumulative_s": 0.173855228 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.00392811, + "cumulative_s": 0.140351227 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.135364915, + "cumulative_s": 0.135364915 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.13526522000000002, + "cumulative_s": 0.13526522000000002 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.022988216000000002, + "cumulative_s": 0.120382891 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.0029038360000000004, + "cumulative_s": 0.100316427 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0066481610000000005, + "cumulative_s": 0.095810242 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.322e-05, + "cumulative_s": 0.071255553 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000491966, + "cumulative_s": 0.071190821 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.0018419290000000002, + "cumulative_s": 0.059699071000000006 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.028139794000000003, + "cumulative_s": 0.05621045400000001 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00449463, + "cumulative_s": 0.054997455 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.3132e-05, + "cumulative_s": 0.044934772000000005 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.04e-05, + "cumulative_s": 0.044769356 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00029332500000000004, + "cumulative_s": 0.044711865 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.0765e-05, + "cumulative_s": 0.043194991 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.1752e-05, + "cumulative_s": 0.042894326 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000159177, + "cumulative_s": 0.042746834000000004 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.041607938000000004, + "cumulative_s": 0.041607938000000004 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.008252658000000001, + "cumulative_s": 0.040041982000000004 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000482493, + "cumulative_s": 0.03994281 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 9.111000000000001e-06, + "cumulative_s": 0.038156843 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000183389, + "cumulative_s": 0.038138882 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00014835, + "cumulative_s": 0.037599496 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009579927, + "cumulative_s": 0.037474547000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.299800000000001e-05, + "cumulative_s": 0.037391400000000005 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 9.556400000000001e-05, + "cumulative_s": 0.037246832 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.012964957000000001, + "cumulative_s": 0.037165284 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.009765559, + "cumulative_s": 0.036822453000000005 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.020222131, + "cumulative_s": 0.036440585000000005 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.01792579, + "cumulative_s": 0.036150664 + }, + { + "file": "threading.py", + "line": 1115, + "function": "join", + "primitive_calls": 3, + "calls": 3, + "self_s": 1.0702e-05, + "cumulative_s": 0.034897291000000004 + }, + { + "file": "threading.py", + "line": 1153, + "function": "_wait_for_tstate_lock", + "primitive_calls": 40, + "calls": 40, + "self_s": 3.3805e-05, + "cumulative_s": 0.034835415 + } + ], + "path_functions": [ + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.092276757, + "cumulative_s": 0.31229900600000005 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.157873689, + "cumulative_s": 0.19941302700000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0066481610000000005, + "cumulative_s": 0.095810242 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000291077, + "cumulative_s": 0.006951125000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.073455617999997, + "warm_predict_median_s": 0.7381084390000012 + }, + { + "strategy": "legacy_cpu", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 4.589201268000011, + "predict_s": 0.7389601649999804, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + }, + { + "phase": "warm_1", + "fit_s": 2.579780000999989, + "predict_s": 0.7394124440000098, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + }, + { + "phase": "warm_2", + "fit_s": 2.576371607999988, + "predict_s": 0.7371488309999847, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + }, + { + "phase": "warm_3", + "fit_s": 2.5762136689999977, + "predict_s": 0.7363390519999768, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.6643025470000055, + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.0556e-05, + "cumulative_s": 2.6641945920000003 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.8737000000000005e-05, + "cumulative_s": 2.6641210660000003 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.003433681, + "cumulative_s": 2.664081454 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000338967, + "cumulative_s": 2.213749368 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 8.5711e-05, + "cumulative_s": 2.2133030320000002 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00014932200000000002, + "cumulative_s": 2.213217321 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 2.209891105, + "cumulative_s": 2.21294899 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.001359697, + "cumulative_s": 0.408372974 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.006098101000000001, + "cumulative_s": 0.405244812 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0009083040000000001, + "cumulative_s": 0.141499953 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.067177284, + "cumulative_s": 0.140171389 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000539861, + "cumulative_s": 0.089100881 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.07813046900000001, + "cumulative_s": 0.087835455 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.006211664, + "cumulative_s": 0.07758426900000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.9104000000000004e-05, + "cumulative_s": 0.067857368 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000341415, + "cumulative_s": 0.067800765 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.005326325000000001, + "cumulative_s": 0.06716321 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.059133630000000006, + "cumulative_s": 0.060523526 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.047846761, + "cumulative_s": 0.057895984000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.1531000000000005e-05, + "cumulative_s": 0.04904643 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.519300000000001e-05, + "cumulative_s": 0.048921504000000005 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00010903800000000001, + "cumulative_s": 0.046290918 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.037756935000000005, + "cumulative_s": 0.046041483 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.9548e-05, + "cumulative_s": 0.042263017 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.9572e-05, + "cumulative_s": 0.042146034000000006 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00017293900000000002, + "cumulative_s": 0.042094452000000004 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.2229e-05, + "cumulative_s": 0.041083586000000005 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.2871000000000002e-05, + "cumulative_s": 0.040909877000000004 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000159595, + "cumulative_s": 0.040833904000000004 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00039617000000000005, + "cumulative_s": 0.037776929 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.891000000000001e-06, + "cumulative_s": 0.034357262 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.961000000000001e-06, + "cumulative_s": 0.034345166 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000295547, + "cumulative_s": 0.034327876 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 183, + "calls": 196, + "self_s": 0.000848835, + "cumulative_s": 0.031018722000000002 + }, + { + "file": "fromnumeric.py", + "line": 51, + "function": "_wrapfunc", + "primitive_calls": 454, + "calls": 466, + "self_s": 0.0010726260000000001, + "cumulative_s": 0.030291076000000004 + }, + { + "file": "_arraysetops_impl.py", + "line": 348, + "function": "_unique1d", + "primitive_calls": 183, + "calls": 196, + "self_s": 0.002021194, + "cumulative_s": 0.029978446000000002 + }, + { + "file": "fromnumeric.py", + "line": 2241, + "function": "clip", + "primitive_calls": 35, + "calls": 38, + "self_s": 0.00020268, + "cumulative_s": 0.029697490000000003 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.002153607, + "cumulative_s": 0.0291893 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 35, + "calls": 38, + "self_s": 0.00019975800000000002, + "cumulative_s": 0.028696862 + }, + { + "file": "numeric.py", + "line": 324, + "function": "full", + "primitive_calls": 1022, + "calls": 1022, + "self_s": 0.004297414, + "cumulative_s": 0.02593753 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00039617000000000005, + "cumulative_s": 0.037776929 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00025717400000000004, + "cumulative_s": 0.000367477 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.576371607999988, + "warm_predict_median_s": 0.7371488309999847 + }, + { + "strategy": "legacy_cuda", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 2.4354087189999802, + "predict_s": 0.16527082900000778, + "metrics": { + "nll": 1.0963920390205588, + "crps": 0.40381986439129747, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "7853e99ce91d1caefe61e03e9fb8705e93e73f201ba215f916d259fc66d63e88", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 0.14955579600001556, + "predict_s": 0.06882309399998121, + "metrics": { + "nll": 1.096392038730512, + "crps": 0.4038198643337002, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "2034f677ab0c8c0019aadb670ea0ec579bf30407311fbafa6fc418570f8eba8e", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 0.14971201000000178, + "predict_s": 0.06821643999998628, + "metrics": { + "nll": 1.0963920370917704, + "crps": 0.4038198643611133, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "65f453c8633b9e048429f5946c57fb66694fecbd71053ab71bbe9aee47d169f3", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 0.14876463799998874, + "predict_s": 0.06689186200000563, + "metrics": { + "nll": 1.0963920323183276, + "crps": 0.40381986019310173, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "7362d29aac04da618bee053dc227934ebd24da7605ccb7726152a9b56bef95f1", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.40463590499999214, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 51, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0023008390000000003, + "cumulative_s": 0.544200494 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0007357770000000001, + "cumulative_s": 0.43110970000000004 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.004093902, + "cumulative_s": 0.41554110000000005 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0036306290000000002, + "cumulative_s": 0.249073288 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.031232655, + "cumulative_s": 0.22824374200000003 + }, + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 7.1127e-05, + "cumulative_s": 0.21788083600000002 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 5.4481e-05, + "cumulative_s": 0.217875861 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.0022298310000000003, + "cumulative_s": 0.18556539800000002 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.4001e-05, + "cumulative_s": 0.172870351 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000361559, + "cumulative_s": 0.172816979 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.033017004, + "cumulative_s": 0.12017947200000001 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4220, + "calls": 4220, + "self_s": 0.019070085, + "cumulative_s": 0.11787928600000001 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.3037e-05, + "cumulative_s": 0.112877977 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 9.5036e-05, + "cumulative_s": 0.11275547200000001 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.4192e-05, + "cumulative_s": 0.105929828 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.1472e-05, + "cumulative_s": 0.105650822 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00023030000000000001, + "cumulative_s": 0.105589071 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.7299e-05, + "cumulative_s": 0.104348726 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.6186000000000003e-05, + "cumulative_s": 0.104063732 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00018117600000000001, + "cumulative_s": 0.10391439400000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 49, + "calls": 49, + "self_s": 0.096680023, + "cumulative_s": 0.096680023 + }, + { + "file": "devicearray.py", + "line": 66, + "function": "__init__", + "primitive_calls": 316, + "calls": 316, + "self_s": 0.0015282910000000002, + "cumulative_s": 0.095287618 + }, + { + "file": "api.py", + "line": 116, + "function": "to_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 5.8457000000000006e-05, + "cumulative_s": 0.089701953 + }, + { + "file": "devicearray.py", + "line": 910, + "function": "auto_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 7.524200000000001e-05, + "cumulative_s": 0.08925210900000001 + }, + { + "file": "devicearray.py", + "line": 850, + "function": "from_array_like", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.8885e-05, + "cumulative_s": 0.085630154 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.485e-06, + "cumulative_s": 0.07720067900000001 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.0517e-05, + "cumulative_s": 0.07718672800000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00020955800000000002, + "cumulative_s": 0.076961294 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1521, + "calls": 1534, + "self_s": 0.0010068660000000001, + "cumulative_s": 0.060388831000000004 + }, + { + "file": "contextlib.py", + "line": 132, + "function": "__enter__", + "primitive_calls": 617, + "calls": 617, + "self_s": 0.000497968, + "cumulative_s": 0.057902739 + }, + { + "file": "devices.py", + "line": 111, + "function": "ensure_context", + "primitive_calls": 1232, + "calls": 1232, + "self_s": 0.0025574200000000004, + "cumulative_s": 0.057752636 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.024927234000000003, + "cumulative_s": 0.055946163 + }, + { + "file": "driver.py", + "line": 1206, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.00020986100000000002, + "cumulative_s": 0.048827512 + }, + { + "file": "driver.py", + "line": 929, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.0010272130000000001, + "cumulative_s": 0.048378617000000006 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2151, + "calls": 2151, + "self_s": 0.0025264940000000002, + "cumulative_s": 0.047021374000000005 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00012765, + "cumulative_s": 0.044627664000000004 + }, + { + "file": "_trainer.py", + "line": 105, + "function": "_bin_features", + "primitive_calls": 1, + "calls": 1, + "self_s": 8.414000000000001e-06, + "cumulative_s": 0.044170287 + }, + { + "file": "_array.py", + "line": 175, + "function": "array", + "primitive_calls": 1, + "calls": 1, + "self_s": 7.499500000000001e-05, + "cumulative_s": 0.044161873000000004 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0018499380000000002, + "cumulative_s": 0.043988965000000005 + }, + { + "file": "_cuda.py", + "line": 1691, + "function": "to_device", + "primitive_calls": 1, + "calls": 1, + "self_s": 1.0714000000000001e-05, + "cumulative_s": 0.042885299 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0007357770000000001, + "cumulative_s": 0.43110970000000004 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000174942, + "cumulative_s": 0.0280454 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.002016893, + "cumulative_s": 0.021300568000000002 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 0.14955579600001556, + "warm_predict_median_s": 0.06821643999998628 + }, + { + "strategy": "experimental_cuda", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 19.776400911999986, + "predict_s": 0.7434680339999886, + "metrics": { + "nll": 1.0963920042722604, + "crps": 0.40381985167085926, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "7520d5eb0274ae574658ad38c0947ef157e75b9d708d5a2f22c14e665d67d05b", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 2.1236640390000048, + "predict_s": 0.7301223449999839, + "metrics": { + "nll": 1.0963919834289706, + "crps": 0.40381984563159234, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "0d4078c2355f595148b4db046198edbea0d5a7a2090577ff160fd6ef43c83384", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.0732637899999986, + "predict_s": 0.7403117650000297, + "metrics": { + "nll": 1.0963919892340488, + "crps": 0.4038198524748159, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "df0b04916a49e63759a96ebde1583e418d16c31bb7062281d4a1c7d0f0fef73b", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 2.0727385769999955, + "predict_s": 0.7406154930000071, + "metrics": { + "nll": 1.0963919804478686, + "crps": 0.403819842249871, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "631c317d746372ee994492fcc10ad70da95edf7052b8082e87655f7f7bb825db", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.4618966560000217, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 334430208, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 0, + "samples": 388, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 0.001142117, + "cumulative_s": 2.460792719 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1599, + "calls": 1733, + "self_s": 0.676329394, + "cumulative_s": 1.6073704100000001 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.071784359, + "cumulative_s": 0.24427725300000003 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.15815553300000001, + "cumulative_s": 0.19950250900000002 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.093771609, + "cumulative_s": 0.19655925100000002 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.13233034400000002, + "cumulative_s": 0.191916621 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.090016313, + "cumulative_s": 0.176802666 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 386, + "calls": 386, + "self_s": 0.166234149, + "cumulative_s": 0.166234149 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.125642951, + "cumulative_s": 0.143055174 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.003819532, + "cumulative_s": 0.13994895100000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.13328056600000002, + "cumulative_s": 0.13328056600000002 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002875733, + "cumulative_s": 0.122506681 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.023586182, + "cumulative_s": 0.111594514 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009265557, + "cumulative_s": 0.077575427 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006342599, + "cumulative_s": 0.071611468 + }, + { + "file": "basic.py", + "line": 12, + "function": "copyto", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.026854211000000003, + "cumulative_s": 0.068252567 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.5329e-05, + "cumulative_s": 0.067272342 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00046284, + "cumulative_s": 0.06717740600000001 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.028724492, + "cumulative_s": 0.057814764000000005 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.887700000000001e-05, + "cumulative_s": 0.048625752 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000269146, + "cumulative_s": 0.048432242 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.0379000000000003e-05, + "cumulative_s": 0.042423906000000004 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.042406200000000005, + "cumulative_s": 0.042406200000000005 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 9.6025e-05, + "cumulative_s": 0.042275163000000004 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00011101600000000001, + "cumulative_s": 0.042048261 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.0015341, + "cumulative_s": 0.041008377000000006 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.9398000000000004e-05, + "cumulative_s": 0.040749582 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 5.9514000000000007e-05, + "cumulative_s": 0.040499914000000005 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 6.7607e-05, + "cumulative_s": 0.040288097 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.014391409, + "cumulative_s": 0.039912539000000004 + }, + { + "file": "from_data.py", + "line": 56, + "function": "asarray", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.00028669300000000003, + "cumulative_s": 0.037836521000000005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.017466973, + "cumulative_s": 0.037543537 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018090956000000002, + "cumulative_s": 0.036606449 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000295285, + "cumulative_s": 0.036391303 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.01946979, + "cumulative_s": 0.035083774000000005 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.188e-06, + "cumulative_s": 0.034272722000000005 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00015359, + "cumulative_s": 0.034261522 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.9435e-05, + "cumulative_s": 0.033780059 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000156205, + "cumulative_s": 0.033228077 + }, + { + "file": "_objectives.py", + "line": 161, + "function": "_step_device", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000220671, + "cumulative_s": 0.032755751 + } + ], + "path_functions": [ + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.071784359, + "cumulative_s": 0.24427725300000003 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.15815553300000001, + "cumulative_s": 0.19950250900000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006342599, + "cumulative_s": 0.071611468 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000156205, + "cumulative_s": 0.033228077 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00032441500000000004, + "cumulative_s": 0.007795248 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.0732637899999986, + "warm_predict_median_s": 0.7403117650000297 + }, + { + "strategy": "extensions_cuda", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 20.649465076000013, + "predict_s": 0.731717524999965, + "metrics": { + "nll": 1.494126317810867, + "crps": 0.6068144263657685, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "4fdc57a17b850f2bc3ebc76f142e4db66685b87161989d18985c16ffc1086615", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 2.0641170810000062, + "predict_s": 0.7319299330000035, + "metrics": { + "nll": 1.4941263170692758, + "crps": 0.6068144262539682, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "4fdc57a17b850f2bc3ebc76f142e4db66685b87161989d18985c16ffc1086615", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.0679065759999844, + "predict_s": 0.7315133530000253, + "metrics": { + "nll": 1.494126317810867, + "crps": 0.6068144263657685, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "4fdc57a17b850f2bc3ebc76f142e4db66685b87161989d18985c16ffc1086615", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 2.0701795020000304, + "predict_s": 0.7302357700000357, + "metrics": { + "nll": 1.4941263226742383, + "crps": 0.6068144272363147, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "fbacc07489c4688599856f30dcff7eebc729727ab1158dbcb9a073f144ee28a5", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.506680313000004, + "memory": { + "scope": "5 ms sampled device-wide usage during separate warm profile fit; includes contexts/other allocations; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 396, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 2044928 + }, + "top_host_functions": [ + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1607, + "calls": 1765, + "self_s": 0.724828782, + "cumulative_s": 1.5873694420000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.080709873, + "cumulative_s": 0.288574048 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.13319186300000002, + "cumulative_s": 0.19423113 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.090581004, + "cumulative_s": 0.186454835 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.140421911, + "cumulative_s": 0.180755473 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 394, + "calls": 394, + "self_s": 0.176961381, + "cumulative_s": 0.176961381 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.08765492600000001, + "cumulative_s": 0.173275764 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.0039386560000000004, + "cumulative_s": 0.163570355 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.141679793, + "cumulative_s": 0.15836707 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.134696482, + "cumulative_s": 0.148740485 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.023873024000000003, + "cumulative_s": 0.127810221 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0069120710000000005, + "cumulative_s": 0.11708367600000001 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002847464, + "cumulative_s": 0.10029431500000001 + }, + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 2, + "self_s": 0.00029062500000000003, + "cumulative_s": 0.083612267 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.3908e-05, + "cumulative_s": 0.07375967 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000399118, + "cumulative_s": 0.073695381 + }, + { + "file": "ranges.py", + "line": 9, + "function": "arange", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.01032454, + "cumulative_s": 0.06736837700000001 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.027952903, + "cumulative_s": 0.055984608000000005 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004368802000000001, + "cumulative_s": 0.055577857 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.3607e-05, + "cumulative_s": 0.05417202300000001 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.901300000000001e-05, + "cumulative_s": 0.05399193 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.0018085680000000002, + "cumulative_s": 0.053307378 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.9605000000000002e-05, + "cumulative_s": 0.046479266000000005 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.3119e-05, + "cumulative_s": 0.046306406 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000261561, + "cumulative_s": 0.046225386 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.5429e-05, + "cumulative_s": 0.044908929 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.5154e-05, + "cumulative_s": 0.044574633 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000194457, + "cumulative_s": 0.044401449 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.042228452, + "cumulative_s": 0.042228452 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.008204764, + "cumulative_s": 0.039954025000000004 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.009942488000000001, + "cumulative_s": 0.038952445 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 9.004e-06, + "cumulative_s": 0.038535615 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00017072200000000002, + "cumulative_s": 0.038517328 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00013891, + "cumulative_s": 0.038132657 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00044125100000000003, + "cumulative_s": 0.038125968 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.00912755, + "cumulative_s": 0.03669244 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.013011387000000001, + "cumulative_s": 0.036524966 + }, + { + "file": "threading.py", + "line": 1115, + "function": "join", + "primitive_calls": 3, + "calls": 3, + "self_s": 6.9224e-05, + "cumulative_s": 0.035844038 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.017426360000000002, + "cumulative_s": 0.035797423 + }, + { + "file": "threading.py", + "line": 1153, + "function": "_wait_for_tstate_lock", + "primitive_calls": 40, + "calls": 40, + "self_s": 3.3886e-05, + "cumulative_s": 0.035622409 + } + ], + "path_functions": [ + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.080709873, + "cumulative_s": 0.288574048 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.140421911, + "cumulative_s": 0.180755473 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0069120710000000005, + "cumulative_s": 0.11708367600000001 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00029520400000000003, + "cumulative_s": 0.0071221060000000004 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.0679065759999844, + "warm_predict_median_s": 0.7315133530000253 + } + ], + "frozen_baseline_cells": [ + { + "backend": "cpu", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.397831454999999, + "predict_params_s": 0.6745909410000017, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4014690210000005, + "predict_params_s": 0.6882345490000006, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 3.2421392509999976, + "predict_params_s": 0.19642429199999967, + "metrics": { + "nll": 1.0944737563673146, + "crps": 0.39859095157938995, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "f30ad0f19907a419c4106ddb23b3ed388782751464b971fb3ae4e231ef84368c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14386336900000174, + "predict_params_s": 0.0673137920000002, + "metrics": { + "nll": 1.0944737689342998, + "crps": 0.39859095595416205, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "a0feedfd12a1e785bd4b8fe1a82bf04da2a069ece3b8c37d812db27ce3af2963", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.106432963000003, + "predict_params_s": 0.6825809110000023, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 3.026923318999998, + "predict_params_s": 0.6765377500000014, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.450299663999999, + "predict_params_s": 0.01670187599999906, + "metrics": { + "nll": 1.0944737574085481, + "crps": 0.39859095178535353, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "4ab822ab4020fe3163e0664f5c51b755060bffa203f544e45148aa66544d4aeb" + }, + { + "phase": "repeat_fit", + "fit_s": 0.22371609900000067, + "predict_params_s": 0.01638825799999921, + "metrics": { + "nll": 1.0944737612758777, + "crps": 0.3985909545795274, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991604804992676, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "794edadbc23c07f1590fc520e02046cda972173bd5c4fca1e3ca8566ca5dadb5", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.533779657000004, + "predict_params_s": 0.6762787880000047, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4129718869999977, + "predict_params_s": 0.6843157980000001, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.407129374, + "predict_params_s": 0.16393877899999865, + "metrics": { + "nll": 1.113434598491401, + "crps": 0.4100669511518961, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "9111616954b6fb780447600cf7aec943b45d3e575b16cf188d1e03fc0e81ae1b" + }, + { + "phase": "repeat_fit", + "fit_s": 0.16078772999999558, + "predict_params_s": 0.06733073400000222, + "metrics": { + "nll": 1.1134345862813968, + "crps": 0.4100669440537582, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "26d571b4fae82b7b516354f105c2e48465aabc290c649e17b1450ff0274c8319", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.032100342, + "predict_params_s": 0.6659178060000102, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 3.0121408979999984, + "predict_params_s": 0.6714607630000131, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.386929182000003, + "predict_params_s": 0.01657929299999239, + "metrics": { + "nll": 1.1134345775848313, + "crps": 0.41006693913527403, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "f0446a567cc7ce8929da5b47732e69a48ba8fd76640e21a1c83b260e6094f652" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23034155599999906, + "predict_params_s": 0.016565403999990735, + "metrics": { + "nll": 1.1134345986050809, + "crps": 0.41006695480534144, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.4206825494766235, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.242552638053894, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872674226760864, + 1.1751006841659546, + 1.1648061275482178, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "45b3c964270056fd4530962345d490834970cdb3fffb7002c7f743235f090923", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.4131726219999905, + "predict_params_s": 0.6596325430000007, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 2.385363081999998, + "predict_params_s": 0.6809927400000078, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.4124752700000016, + "predict_params_s": 0.1630236369999949, + "metrics": { + "nll": 1.0963920238037668, + "crps": 0.4038198588211825, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "29253dd5874532fcafc8c8c2bc392181213ae0ea9cbeef4d6297378ac1f3113c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14519270999998923, + "predict_params_s": 0.06746661900000106, + "metrics": { + "nll": 1.096392045323513, + "crps": 0.40381986836337114, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "3f816168712d3b935609d80392fe074faac068c4372a1ac25d2267594581301c", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.201210966000005, + "predict_params_s": 0.8796572620000092, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 3.2026938079999923, + "predict_params_s": 0.6871226760000013, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.509326623000007, + "predict_params_s": 0.016671253000012598, + "metrics": { + "nll": 1.096392041715409, + "crps": 0.403819866890205, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.201305627822876, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.1229389905929565, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "9af00eccc01727765f72dde3b81530643aa5427a31ade9b25451d0dcdf00f7cc" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23239498899999944, + "predict_params_s": 0.01806297500000653, + "metrics": { + "nll": 1.0963920449466849, + "crps": 0.4038198678278385, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "a5e059e04cdc02fa03b1f05dde2cd3b5264ca34bf3ca4f27fd6d86ae372fa712", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + } + ], + "unsupported_comparisons": [ + "strict CUDA eval/callbacks" + ], + "value_summary": { + "per_seed": [ + { + "seed": 0, + "quality_pass": true, + "legacy_matches_frozen_quality": true, + "warm_fit_ratio": 12.99481709396146 + }, + { + "seed": 1, + "quality_pass": true, + "legacy_matches_frozen_quality": true, + "warm_fit_ratio": 14.06330806871822 + }, + { + "seed": 2, + "quality_pass": true, + "legacy_matches_frozen_quality": true, + "warm_fit_ratio": 13.862811375092296 + } + ], + "warm_fit_medians_s": { + "legacy_cpu": 2.4481368569999944, + "legacy_cuda": 0.14955579600001556, + "experimental_cuda": 2.0786940920000063, + "extensions_cuda": 2.073455617999997 + }, + "default_fit_ratio": 13.89912091404194, + "profiling_triggered": true, + "quality_pass": true, + "performance_budget_pass": false, + "scope": "resident fit only; eval/callbacks unsupported on strict GPU; T4 seconds are not billed dollars" + } + }, + "remote_function_wall_s": 297.296047688, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T184856Z-dcd49569/README.md b/benchmarks/results/foundation/20260905T184856Z-dcd49569/README.md new file mode 100644 index 0000000..c98877a --- /dev/null +++ b/benchmarks/results/foundation/20260905T184856Z-dcd49569/README.md @@ -0,0 +1,80 @@ +# P7 isolated host profiling follow-up + +Clean source `9bb1ff38cce08df5631cd0aa7fcd3a84b20cf8cd`. T4: **3 passed / 0 skipped**. +Four seed-0 strategies, one compilation warmup, then a host-profile fit and a +separate memory-only fit. Quality matches the corresponding original cells. +The [original 12-cell timing matrix](../20260905T183820Z-3c245f2d/README.md) remains +unchanged; this diagnostic run neither replaces its times nor changes its failed +1.2 performance budget. Its results hash is recorded in profile_parent. + +## Synchronized inclusive diagnostics + +| Strategy | Profile fit wall s | Objective boundary s | Tree/session boundary s | LevelWiseBuilder inside session s | +|---|---:|---:|---:|---:| +| Legacy CPU | 2.3961 | .0146 | — | — | +| Legacy CUDA | .2448 | .0200 | .1751 (native trees) | — | +| Default experimental CUDA | 2.2601 | .0885 | 2.1226 | 1.5880 | +| Independent A+B+C CUDA | 2.2135 | .1045 | 2.0674 | 1.5527 | + +Each objective runs 30 times and each tree path 60 times. Nested timers overlap: +the .0348 s underlying built-in objective is already inside the default's .0885 s +objective boundary, and LevelWiseBuilder is inside the tree/session boundary. +Synchronization and cProfile add overhead; these are not production fit times. + +The default spends about 94% of this diagnostic wall time in its tree/session +boundary, with about 1.588 s inside the builder and .535 s elsewhere in that +boundary. Objective arithmetic is not the dominant cost. The no-sampler profile +shows 2,400 CuPy any calls and 2,190 ndarray all calls on this path, along with +180 histogram/split/partition calls and compact-tree validation. These counts +support investigating repeated validation and synchronization, but do not prove +which CUDA kernel or check would deliver a particular speedup. + +Source inspection shows device copies of borrowed inputs, scalar validation, +compact tree snapshots, and independent cache-validation traversal. Keep those +correctness contracts. First investigate batching/reusing validation or reducing +redundant traversal/synchronization with independent parity tests; do not simply +remove checks or route an explicit external builder through the legacy builder. +Only rerun affected comparisons after an actual change; no such core optimization +is included in this evidence. This is a design review, not a claimed fix. + +## Transfer and memory boundary + +Each CUDA profile records 300 named compact D2H wrappers: Numba copy_to_host for +legacy, CuPy asnumpy for the experimental paths. Default asnumpy cumulative host +time is .0067 s. Named wrappers exclude internal/scalar traffic and are not a +complete transfer audit. No CUDA trace was captured; nsys is unavailable. + +Memory now runs without any host profiler in a separate warm fit. Default +experimental device-wide sampled usage starts at 332,333,056 bytes and reaches +334,430,208 bytes (2 MiB delta). Legacy and A+B+C show zero sampled delta. Those +figures include contexts, allocator caches and other device allocations; a 5 ms +sample can miss peaks. Zero delta does not mean no training allocation, and this +is not an exact per-fit peak. No GPU billing/cost dollars are inferred. + +The original combined sampler/profile had inconsistent inclusive attribution. +This follow-up removes memGetInfo/sampler-lock contamination and adds explicit +synchronized boundary timers. Retain the original profiles with their limitation; +do not use their time percentages for causal claims. + +Core and both plugin wheel hashes exactly match P6 and original P7. Python +3.12.1, Tesla T4, driver 580.95.05, CUDA runtime 12090; full package/environment +and dataset provenance in manifest/results. Requested 2 CPU / 8192 MiB, threads 2, +CPU model unknown. Pytest 79.98 s; remote function 84.03 s. One T4, 600-second +limit, no retries. These function times include warmup/profiling, not billing. + +Reproduce from the source revision: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite value_profile +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_value_profile +``` + +Validate offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T184856Z-dcd49569 +``` + +Verdict unchanged: default quality passes, GPU performance budget fails, G5 +external adoption unverified. Retain the experimental research API; do not replace +the legacy CUDA path or claim a generally superior GPU foundation. diff --git a/benchmarks/results/foundation/20260905T184856Z-dcd49569/junit.xml b/benchmarks/results/foundation/20260905T184856Z-dcd49569/junit.xml new file mode 100644 index 0000000..cd5294e --- /dev/null +++ b/benchmarks/results/foundation/20260905T184856Z-dcd49569/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T184856Z-dcd49569/manifest.json b/benchmarks/results/foundation/20260905T184856Z-dcd49569/manifest.json new file mode 100644 index 0000000..d8b8ac5 --- /dev/null +++ b/benchmarks/results/foundation/20260905T184856Z-dcd49569/manifest.json @@ -0,0 +1,123 @@ +{ + "schema_version": 1, + "suite": "value_profile", + "test_files": [ + "test_smoke.py", + "test_value.py" + ], + "source_sha": "9bb1ff38cce08df5631cd0aa7fcd3a84b20cf8cd", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "dataset.py": "cee302d02c1f82e54c19693fdbff8a7671955f816cccb6fc5902860414109e2a", + "baseline_worker.py": "3abb07c372d2fae3ffc8973ff1b1220d207302b9f04282514762eb907e363e88", + "housing.json": "d511bf1b61c6e515598a91cc468b9c860dcde60f9e1e7a3e2570e9d8e8c3bc5b", + "test_value.py": "81f6524faa7f20a7f5ee6791fc0cbd3d75119dd021a1a9a28a53e1105ec6b060", + "value_worker.py": "bbcacfd9e151acc67338241a18a6be6df9aabc9640d0245899625e1a39104e1f", + "value_protocol.py": "8d560b54c53e8b00ca37d91aadb55215776e628406bdd201d463a3464a40f332", + "p2_baseline.json": "1d024133ecd1975b7854eb7601b3a3a571981613aae8dd785dc98181ace78818", + "value_parent.json": "439a3b05d4b08e69453dbfa949d2b75ed98ca31080aadd1002cbaa503ee57d35", + "cal_housing.tgz": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_value_profile" + ], + "gpu": "T4", + "timeout_s": 600, + "retries": 0, + "dataset": { + "name": "California Housing", + "source_url": "https://ndownloader.figshare.com/files/5976036", + "archive_sha256": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681", + "member": "CaliforniaHousing/cal_housing.data", + "features": [ + "MedInc", + "HouseAge", + "AveRooms", + "AveBedrms", + "Population", + "AveOccup", + "Latitude", + "Longitude" + ], + "shape": [ + 20640, + 8 + ], + "target_unit": "100,000 USD", + "arrays_sha256": "34fc72e53a6329a89a9ae792e92f37853cea98adfbc6feea28cc08dc06f68ca1", + "dtype": "little-endian float32", + "split": "numpy.default_rng(seed), permutation, 60/20/20", + "split_sha256": { + "0": [ + "7d07544c6c3fddd72d0cfc8d0aa0c19cf00250f1890420bdb10bd80efb0c9c04", + "5f6d9931d5a5239c7933da9bb62df2c631e79017658f185742db94f139be358a", + "877744733fe0213e716ca084333f39385c30654169f117f772c53a6af8bcff37" + ], + "1": [ + "99578d3ef329ef275fd41e3834df7dab763525f9374db203b6f748b783eed05a", + "a161a60cd85d1ec54cbfd590f6befdb58b1a9a695c635bfbb562b4064d265caf", + "039c4fc07f6596a3cfdf73cb55820de4baa13f93251ede4c215796f283d46256" + ], + "2": [ + "51ab6cbe0019b5bea9f9da8a81b3fafa874d8c992062f65ee8d1b09a3b7898a1", + "14509d20c7d87948ac6ea9060c476995583dd130af0ff928a9c24709e5ecf9d4", + "ff6426d360c75b637dd866cd73d9fd1b9fcf310a886dc05fbac0f80dcf256ec8" + ] + } + }, + "extension_wheels": { + "openboost_example_normal_fisher-0.2.0-py3-none-any.whl": "47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b", + "openboost_example_bounded_leaves-0.2.0-py3-none-any.whl": "dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3" + }, + "extension_sources": { + "examples/extensions/normal_fisher/README.md": "78a5142eaf92a81df9c5908861a1ce66e5ae8b03bf753e0b84a834c5f2a5d0eb", + "examples/extensions/normal_fisher/pyproject.toml": "ab70495fafa430dfb1377a873cbb821c9484c9008cea26575fa3c3fa2a91e40d", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "cabafae48d9ad251eea62230b1d38ba7407fbb113dec116b3e840c05e032afd3", + "examples/extensions/normal_fisher/tests/test_normal.py": "6dcb48f9434879b054fe9a9e8b6fca5f4ba01782d8c45ed4c566a8306ee867c3", + "examples/extensions/bounded_leaves/README.md": "749f68fafb9e8cb5911c16aa79ab7a82593599e5c196c20be3ec9923172144e9", + "examples/extensions/bounded_leaves/pyproject.toml": "8731c3fa03f03341fcc987b449c04658c5bde3e7a4d24772e3bc2199ceb55eac", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "d53927c63c71ea3cf37083e0d71d11aff58941dbed1df430838c2d6b2912efff", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "41339c49f146181811dd0680d2b1abc563448ce35b4a79e8d4f0559c5d949baf" + }, + "value_protocol": { + "seeds": [ + 0 + ], + "strategies": [ + "legacy_cpu", + "legacy_cuda", + "experimental_cuda", + "extensions_cuda" + ], + "mode": "resident", + "repetitions": 1, + "warm_fit_budget_ratio": 1.2, + "extension_bound": 0.5, + "extension_schedule_tau": 1, + "cache_policy": "fresh process and NUMBA/CUPY cache directories per cell; device context startup/imports excluded; driver cache not cleared", + "profile": "isolated synchronized host profile followed by a separate memory-only fit; no speed comparison", + "cost": "T4 seconds only; billing unavailable" + }, + "profile_parent": { + "artifact": "20260905T183820Z-3c245f2d", + "results_sha256": "439a3b05d4b08e69453dbfa949d2b75ed98ca31080aadd1002cbaa503ee57d35", + "scope": "seed 0 only; one warmup then isolated host profile and separate memory fit; original timing matrix retained" + }, + "run_id": "20260905T184856Z-dcd49569", + "modal_image_id": "im-OvA98hIFRpGlKJDqE4h1Xj" +} diff --git a/benchmarks/results/foundation/20260905T184856Z-dcd49569/results.json b/benchmarks/results/foundation/20260905T184856Z-dcd49569/results.json new file mode 100644 index 0000000..7d858d7 --- /dev/null +++ b/benchmarks/results/foundation/20260905T184856Z-dcd49569/results.json @@ -0,0 +1,3120 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 404784209920, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "iniconfig": "2.3.0", + "setuptools": "69.0.3", + "joblib": "1.5.3", + "openboost-example-normal-fisher": "0.2.0", + "pytest-cov": "7.0.0", + "pytest": "9.0.2", + "pluggy": "1.6.0", + "numba-cuda": "0.27.0", + "numba": "0.63.1", + "llvmlite": "0.46.0", + "execnet": "2.1.2", + "fastrlock": "0.8.3", + "numpy": "2.3.5", + "packaging": "25.0", + "cupy-cuda12x": "13.6.0", + "cuda-core": "0.6.0", + "openboost": "1.0.0rc1", + "pip": "23.3.2", + "cuda-pathfinder": "1.4.0", + "pytest-xdist": "3.8.0", + "coverage": "7.13.1", + "Pygments": "2.19.2", + "scipy": "1.16.3", + "cuda-bindings": "13.1.1", + "openboost-example-bounded-leaves": "0.2.0", + "aiohttp": "3.12.7", + "idna": "3.10", + "attrs": "25.3.0", + "certifi": "2025.4.26", + "h2": "4.2.0", + "propcache": "0.3.1", + "aiosignal": "1.3.2", + "protobuf": "6.31.1", + "typing_extensions": "4.13.2", + "hpack": "4.1.0", + "aiohappyeyeballs": "2.6.1", + "frozenlist": "1.6.0", + "yarl": "1.20.0", + "multidict": "6.4.4", + "cbor2": "5.7.0", + "grpclib": "0.4.8", + "hyperframe": "6.1.0" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "9bb1ff38cce08df5631cd0aa7fcd3a84b20cf8cd", + "wheel_sha256": "5dde197f412ffa9a1b0d59e445b043359c7dfef2a6e2170b9d1a849a0969e4ed", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_value.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n3 passed, 17 warnings in 79.98s (0:01:19)\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "value_cells": [ + { + "strategy": "legacy_cpu", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "untimed_warmup", + "fit_s": 4.388223821, + "predict_s": 0.6980395250000004, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.396078825, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.01464178899999169 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.1824000000000005e-05, + "cumulative_s": 2.395989457 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.7714000000000004e-05, + "cumulative_s": 2.395876795 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.002572286, + "cumulative_s": 2.3958275760000003 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000270032, + "cumulative_s": 2.005626486 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 7.156400000000001e-05, + "cumulative_s": 2.005266043 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00012033600000000001, + "cumulative_s": 2.005194479 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 2.0022667540000003, + "cumulative_s": 2.0049618700000003 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0011270940000000001, + "cumulative_s": 0.35098195000000004 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0045467500000000004, + "cumulative_s": 0.3483964 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0006871840000000001, + "cumulative_s": 0.122184946 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.059969076, + "cumulative_s": 0.12110363700000001 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00044848900000000005, + "cumulative_s": 0.081128184 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.07284437, + "cumulative_s": 0.08016343000000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.1664e-05, + "cumulative_s": 0.07005478600000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00036917600000000003, + "cumulative_s": 0.070006817 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.004195743, + "cumulative_s": 0.055560382000000005 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.049238514000000004, + "cumulative_s": 0.05035336 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.7129e-05, + "cumulative_s": 0.0491969 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.0303e-05, + "cumulative_s": 0.049085282 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.0037516320000000004, + "cumulative_s": 0.047671451000000004 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 9.368500000000001e-05, + "cumulative_s": 0.043964585 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.035762180000000005, + "cumulative_s": 0.043751391 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.6577e-05, + "cumulative_s": 0.043541834 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.3865e-05, + "cumulative_s": 0.043432448000000005 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00021488, + "cumulative_s": 0.04339698 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.3146000000000004e-05, + "cumulative_s": 0.042434182 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.4119000000000002e-05, + "cumulative_s": 0.042219422 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000114587, + "cumulative_s": 0.042106670000000006 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.033719258, + "cumulative_s": 0.040938661 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00032779200000000004, + "cumulative_s": 0.036018078 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00040464900000000004, + "cumulative_s": 0.035588696 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 3839, + "calls": 3839, + "self_s": 0.016574455000000002, + "cumulative_s": 0.034304504 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.168e-06, + "cumulative_s": 0.033916023000000003 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.855000000000001e-06, + "cumulative_s": 0.033910387 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000221553, + "cumulative_s": 0.03389907 + }, + { + "file": "_distributions.py", + "line": 211, + "function": "natural_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0045154340000000005, + "cumulative_s": 0.033463252 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 184, + "calls": 196, + "self_s": 0.000626399, + "cumulative_s": 0.027635106000000003 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.001895902, + "cumulative_s": 0.027407094000000003 + }, + { + "file": "_arraysetops_impl.py", + "line": 348, + "function": "_unique1d", + "primitive_calls": 184, + "calls": 196, + "self_s": 0.001522143, + "cumulative_s": 0.026874187 + }, + { + "file": "_distributions.py", + "line": 393, + "function": "nll_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.002158134, + "cumulative_s": 0.024315709 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00040464900000000004, + "cumulative_s": 0.035588696 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00018159800000000002, + "cumulative_s": 0.000276527 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "" + }, + { + "strategy": "legacy_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "untimed_warmup", + "fit_s": 3.2582800850000027, + "predict_s": 0.19482653800000094, + "metrics": { + "nll": 1.094473758382929, + "crps": 0.3985909521255572, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "6f6c2f2b3175007a28b5e6d663f954a133fd07d2a63d01ad76a640ed76c8cff3", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.2448053839999993, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.01996890600001322 + }, + "legacy_native_tree": { + "calls": 60, + "inclusive_s": 0.17513217100000134 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 29, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.4513000000000006e-05, + "cumulative_s": 0.244692648 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.1758e-05, + "cumulative_s": 0.244594155 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000840985, + "cumulative_s": 0.24448012300000002 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 90, + "self_s": 0.0006095250000000001, + "cumulative_s": 0.197722215 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00048687000000000003, + "cumulative_s": 0.17278732900000002 + }, + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0008904430000000001, + "cumulative_s": 0.16499266 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.002885734, + "cumulative_s": 0.16410221700000002 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.003039857, + "cumulative_s": 0.160250302 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.022713841000000002, + "cumulative_s": 0.14814549000000002 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.026941101000000002, + "cumulative_s": 0.098519242 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.1584e-05, + "cumulative_s": 0.072503328 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000407057, + "cumulative_s": 0.072443697 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.0015219810000000002, + "cumulative_s": 0.056876216 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.776900000000001e-05, + "cumulative_s": 0.0522444 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.6692e-05, + "cumulative_s": 0.052070345000000004 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.020159990000000003, + "cumulative_s": 0.045903654 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.629e-05, + "cumulative_s": 0.045530258000000004 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.3886e-05, + "cumulative_s": 0.045402740000000004 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00020918100000000002, + "cumulative_s": 0.045347923000000005 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 6.2864e-05, + "cumulative_s": 0.044011785000000005 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.8120000000000002e-05, + "cumulative_s": 0.043617025000000004 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00019009900000000002, + "cumulative_s": 0.043533890000000006 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4760, + "calls": 4760, + "self_s": 0.012671913, + "cumulative_s": 0.039951451000000006 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.376e-06, + "cumulative_s": 0.036919061 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.204000000000001e-06, + "cumulative_s": 0.036908963 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00031075300000000004, + "cumulative_s": 0.036896006 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1521, + "calls": 1534, + "self_s": 0.0008474400000000001, + "cumulative_s": 0.033927248 + }, + { + "file": "devices.py", + "line": 111, + "function": "ensure_context", + "primitive_calls": 1232, + "calls": 1232, + "self_s": 0.001992553, + "cumulative_s": 0.032410795 + }, + { + "file": "contextlib.py", + "line": 132, + "function": "__enter__", + "primitive_calls": 617, + "calls": 617, + "self_s": 0.000496975, + "cumulative_s": 0.032156635 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2331, + "calls": 2331, + "self_s": 0.0021771110000000002, + "cumulative_s": 0.031794528 + }, + { + "file": "devices.py", + "line": 147, + "function": "_get_or_create_context_uncached", + "primitive_calls": 2015, + "calls": 2015, + "self_s": 0.008317014000000001, + "cumulative_s": 0.028941315000000002 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 1715, + "calls": 1715, + "self_s": 0.0008056430000000001, + "cumulative_s": 0.027974719000000002 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.001443833, + "cumulative_s": 0.025975135 + }, + { + "file": "devicearray.py", + "line": 66, + "function": "__init__", + "primitive_calls": 316, + "calls": 316, + "self_s": 0.0012981610000000001, + "cumulative_s": 0.025313485 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.2688e-05, + "cumulative_s": 0.024625074 + }, + { + "file": "driver.py", + "line": 1206, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.000102701, + "cumulative_s": 0.02368697 + }, + { + "file": "_objectives.py", + "line": 117, + "function": "init_raw", + "primitive_calls": 1, + "calls": 1, + "self_s": 9.66e-06, + "cumulative_s": 0.023413374 + }, + { + "file": "_distributions.py", + "line": 384, + "function": "init_params", + "primitive_calls": 1, + "calls": 1, + "self_s": 2.4211000000000002e-05, + "cumulative_s": 0.023402424 + }, + { + "file": "fromnumeric.py", + "line": 3869, + "function": "std", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.7466e-05, + "cumulative_s": 0.023372805 + }, + { + "file": "_methods.py", + "line": 220, + "function": "_std", + "primitive_calls": 1, + "calls": 1, + "self_s": 7.239800000000001e-05, + "cumulative_s": 0.023053175000000002 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00048687000000000003, + "cumulative_s": 0.17278732900000002 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000120582, + "cumulative_s": 0.018858137 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.0016249970000000002, + "cumulative_s": 0.009190056 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "" + }, + { + "strategy": "experimental_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "untimed_warmup", + "fit_s": 20.050354103, + "predict_s": 0.7574919489999985, + "metrics": { + "nll": 1.0944737176360888, + "crps": 0.39859093546999286, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "082b8fdd40d88b7e3fc9d8feec563f393542f8f79fff5adbb1c6d120c7b6c1a2", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.2600502800000015, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.034819996000017284 + }, + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.08849834600001572 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.5879718709999935 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 2.1226376159999916 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 2097152, + "samples": 378, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000221818, + "cumulative_s": 2.259886205 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.9670000000000004e-05, + "cumulative_s": 2.2592341400000002 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.005169971000000001, + "cumulative_s": 2.2591169420000003 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 180, + "self_s": 0.0032864770000000003, + "cumulative_s": 2.238129256 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.39073413500000004, + "cumulative_s": 2.118225063 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.7686241540000001, + "cumulative_s": 1.583258405 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.144440657, + "cumulative_s": 0.21106090300000002 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.097419186, + "cumulative_s": 0.198264238 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09758266600000001, + "cumulative_s": 0.19208871300000002 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.025336518000000002, + "cumulative_s": 0.14554059 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.0035621470000000003, + "cumulative_s": 0.137454023 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.13115231200000002, + "cumulative_s": 0.13115231200000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.121424951, + "cumulative_s": 0.121424951 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.0026291970000000002, + "cumulative_s": 0.10077272000000001 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006769121, + "cumulative_s": 0.086641164 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.6499e-05, + "cumulative_s": 0.069175395 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00031625800000000003, + "cumulative_s": 0.069098608 + }, + { + "file": "from_data.py", + "line": 56, + "function": "asarray", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.00043729600000000005, + "cumulative_s": 0.06136675900000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.018277052000000002, + "cumulative_s": 0.060739862000000006 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029942715, + "cumulative_s": 0.060545226 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.2517e-05, + "cumulative_s": 0.05117813 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.8422e-05, + "cumulative_s": 0.05102872000000001 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.4545000000000003e-05, + "cumulative_s": 0.043451703 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.2516000000000003e-05, + "cumulative_s": 0.043305266 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00017233, + "cumulative_s": 0.043230834 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.482e-05, + "cumulative_s": 0.042021526000000003 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.7952e-05, + "cumulative_s": 0.041698746 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00017023, + "cumulative_s": 0.041621683 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.04154957, + "cumulative_s": 0.04154957 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.014489361000000001, + "cumulative_s": 0.039753599 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.001423372, + "cumulative_s": 0.039594938 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000311802, + "cumulative_s": 0.039570825000000004 + }, + { + "file": "value_worker.py", + "line": 58, + "function": "sync", + "primitive_calls": 361, + "calls": 361, + "self_s": 0.001104015, + "cumulative_s": 0.039310522 + }, + { + "file": "api.py", + "line": 290, + "function": "synchronize", + "primitive_calls": 361, + "calls": 361, + "self_s": 0.00045689200000000003, + "cumulative_s": 0.037408788000000005 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009340605, + "cumulative_s": 0.036954331 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018234358000000003, + "cumulative_s": 0.036590651 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 631, + "calls": 631, + "self_s": 0.0009296980000000001, + "cumulative_s": 0.036421608 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.020094481, + "cumulative_s": 0.036333977 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 511, + "calls": 511, + "self_s": 0.00035350800000000004, + "cumulative_s": 0.035601445 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.9770000000000005e-06, + "cumulative_s": 0.034870943 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.39073413500000004, + "cumulative_s": 2.118225063 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.7686241540000001, + "cumulative_s": 1.583258405 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006769121, + "cumulative_s": 0.086641164 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00023503800000000002, + "cumulative_s": 0.033294131000000005 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000268881, + "cumulative_s": 0.006706143 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "" + }, + { + "strategy": "extensions_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "untimed_warmup", + "fit_s": 20.819399088000004, + "predict_s": 0.7362119219999954, + "metrics": { + "nll": 1.4836263997432595, + "crps": 0.5995845148900152, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "503d86ce2fed87695ffdb93c18902e908775b3d942e3477bb17f07c663c077c9", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.213516548000001, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.10451695799997651 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.5526578110000742 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 2.0673616990000454 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 383, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 2044928 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000181923, + "cumulative_s": 2.213378399 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 2.7413000000000003e-05, + "cumulative_s": 2.2128930380000003 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.004736416, + "cumulative_s": 2.212863963 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 150, + "self_s": 0.002757158, + "cumulative_s": 2.175776908 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.37828060500000005, + "cumulative_s": 2.063046733 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.758720122, + "cumulative_s": 1.548202187 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.14126655200000002, + "cumulative_s": 0.20576586300000002 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09432905300000001, + "cumulative_s": 0.192034839 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.093298193, + "cumulative_s": 0.184116004 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.024347585, + "cumulative_s": 0.143333132 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.003630985, + "cumulative_s": 0.132970216 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.130341232, + "cumulative_s": 0.130341232 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.128177738, + "cumulative_s": 0.128177738 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006356662000000001, + "cumulative_s": 0.102703006 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002611242, + "cumulative_s": 0.09705190500000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.8706000000000004e-05, + "cumulative_s": 0.07130138800000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000349749, + "cumulative_s": 0.071232443 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004428633, + "cumulative_s": 0.059993166 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.028863671, + "cumulative_s": 0.058385761 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.0016138270000000002, + "cumulative_s": 0.053011812000000005 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.0769e-05, + "cumulative_s": 0.050779816000000005 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.743100000000001e-05, + "cumulative_s": 0.050643531000000006 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.3409000000000002e-05, + "cumulative_s": 0.044555348 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.1121e-05, + "cumulative_s": 0.044406190000000005 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000184, + "cumulative_s": 0.044336653000000004 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.82e-05, + "cumulative_s": 0.04310143 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.2778e-05, + "cumulative_s": 0.042869426 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000114288, + "cumulative_s": 0.042785776000000005 + }, + { + "file": "basic.py", + "line": 13, + "function": "empty", + "primitive_calls": 5460, + "calls": 5460, + "self_s": 0.041247915, + "cumulative_s": 0.041247915 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.010011373, + "cumulative_s": 0.040725237000000004 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00034889000000000003, + "cumulative_s": 0.038133496 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.008124634, + "cumulative_s": 0.036590512000000006 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.012873962000000001, + "cumulative_s": 0.035852811000000005 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.00899276, + "cumulative_s": 0.035534462 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.017627262, + "cumulative_s": 0.035429528 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.019683548000000002, + "cumulative_s": 0.035359137 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 4.194e-06, + "cumulative_s": 0.03519437 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.8630000000000004e-06, + "cumulative_s": 0.035187362 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000243212, + "cumulative_s": 0.035174137 + }, + { + "file": "_batch_split.py", + "line": 165, + "function": "", + "primitive_calls": 900, + "calls": 900, + "self_s": 0.0007848250000000001, + "cumulative_s": 0.027558212000000002 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.37828060500000005, + "cumulative_s": 2.063046733 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.758720122, + "cumulative_s": 1.548202187 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006356662000000001, + "cumulative_s": 0.102703006 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000259251, + "cumulative_s": 0.006651071000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "" + } + ], + "frozen_baseline_cells": [ + { + "backend": "cpu", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.397831454999999, + "predict_params_s": 0.6745909410000017, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4014690210000005, + "predict_params_s": 0.6882345490000006, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 3.2421392509999976, + "predict_params_s": 0.19642429199999967, + "metrics": { + "nll": 1.0944737563673146, + "crps": 0.39859095157938995, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "f30ad0f19907a419c4106ddb23b3ed388782751464b971fb3ae4e231ef84368c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14386336900000174, + "predict_params_s": 0.0673137920000002, + "metrics": { + "nll": 1.0944737689342998, + "crps": 0.39859095595416205, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "a0feedfd12a1e785bd4b8fe1a82bf04da2a069ece3b8c37d812db27ce3af2963", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.106432963000003, + "predict_params_s": 0.6825809110000023, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 3.026923318999998, + "predict_params_s": 0.6765377500000014, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.450299663999999, + "predict_params_s": 0.01670187599999906, + "metrics": { + "nll": 1.0944737574085481, + "crps": 0.39859095178535353, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "4ab822ab4020fe3163e0664f5c51b755060bffa203f544e45148aa66544d4aeb" + }, + { + "phase": "repeat_fit", + "fit_s": 0.22371609900000067, + "predict_params_s": 0.01638825799999921, + "metrics": { + "nll": 1.0944737612758777, + "crps": 0.3985909545795274, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991604804992676, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "794edadbc23c07f1590fc520e02046cda972173bd5c4fca1e3ca8566ca5dadb5", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.533779657000004, + "predict_params_s": 0.6762787880000047, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4129718869999977, + "predict_params_s": 0.6843157980000001, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.407129374, + "predict_params_s": 0.16393877899999865, + "metrics": { + "nll": 1.113434598491401, + "crps": 0.4100669511518961, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "9111616954b6fb780447600cf7aec943b45d3e575b16cf188d1e03fc0e81ae1b" + }, + { + "phase": "repeat_fit", + "fit_s": 0.16078772999999558, + "predict_params_s": 0.06733073400000222, + "metrics": { + "nll": 1.1134345862813968, + "crps": 0.4100669440537582, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "26d571b4fae82b7b516354f105c2e48465aabc290c649e17b1450ff0274c8319", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.032100342, + "predict_params_s": 0.6659178060000102, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 3.0121408979999984, + "predict_params_s": 0.6714607630000131, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.386929182000003, + "predict_params_s": 0.01657929299999239, + "metrics": { + "nll": 1.1134345775848313, + "crps": 0.41006693913527403, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "f0446a567cc7ce8929da5b47732e69a48ba8fd76640e21a1c83b260e6094f652" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23034155599999906, + "predict_params_s": 0.016565403999990735, + "metrics": { + "nll": 1.1134345986050809, + "crps": 0.41006695480534144, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.4206825494766235, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.242552638053894, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872674226760864, + 1.1751006841659546, + 1.1648061275482178, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "45b3c964270056fd4530962345d490834970cdb3fffb7002c7f743235f090923", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.4131726219999905, + "predict_params_s": 0.6596325430000007, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 2.385363081999998, + "predict_params_s": 0.6809927400000078, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.4124752700000016, + "predict_params_s": 0.1630236369999949, + "metrics": { + "nll": 1.0963920238037668, + "crps": 0.4038198588211825, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "29253dd5874532fcafc8c8c2bc392181213ae0ea9cbeef4d6297378ac1f3113c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14519270999998923, + "predict_params_s": 0.06746661900000106, + "metrics": { + "nll": 1.096392045323513, + "crps": 0.40381986836337114, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "3f816168712d3b935609d80392fe074faac068c4372a1ac25d2267594581301c", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.201210966000005, + "predict_params_s": 0.8796572620000092, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 3.2026938079999923, + "predict_params_s": 0.6871226760000013, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.509326623000007, + "predict_params_s": 0.016671253000012598, + "metrics": { + "nll": 1.096392041715409, + "crps": 0.403819866890205, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.201305627822876, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.1229389905929565, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "9af00eccc01727765f72dde3b81530643aa5427a31ade9b25451d0dcdf00f7cc" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23239498899999944, + "predict_params_s": 0.01806297500000653, + "metrics": { + "nll": 1.0963920449466849, + "crps": 0.4038198678278385, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "a5e059e04cdc02fa03b1f05dde2cd3b5264ca34bf3ca4f27fd6d86ae372fa712", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + } + ], + "unsupported_comparisons": [ + "strict CUDA eval/callbacks" + ], + "profile_quality_matches_parent": true + }, + "remote_function_wall_s": 84.033467723, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T193001Z-cece3e8f/README.md b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/README.md new file mode 100644 index 0000000..269b778 --- /dev/null +++ b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/README.md @@ -0,0 +1,36 @@ +# Fixed-slot growth: real CUDA correctness + +Clean source `81acf0bf3b3e2ff5da437cdafe1b1c94169acfac`; core wheel +`8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077`. +Real T4: **7 passed / 0 skipped**. The implementation changes only fixed-slot +split application/frontier construction in LevelWiseBuilder, avoiding boolean +compaction while retaining all validation and numerical primitives. + +The suite checks histogram/split/leaf original-row oracles, complete trees, +weighted two-channel CPU/CUDA composition, bounded leaves, saved CPU predictions, +and actual strict GPU Normal/Poisson fits in ordinary/natural modes. It also +checks explicit external builder dispatch, mutation/invalid-statistics/cached +prediction failures and rollback. Maximum adapter raw CPU/CUDA error 1.19e-7; +actual Normal NLL agrees and CRPS differences stay below 7e-9. No fallback. + +Named transfers remain at the existing expected counts (85 compact arrays in +the builder test, 40 in the strict trainer test). This does not constitute a +complete transfer trace. nsys is unavailable. No performance claim follows from +correctness duration; the frozen P7 value matrix is the next separate check. + +T4, driver 580.95.05; Python 3.12.1, CUDA runtime/package/CPU/RAM/thread details +are in results.json. Pytest 38.78 s, function 42.88 s; 300-second limit, no retries. +Only wheel and allowlisted tests/oracles/manifest uploaded; no source mount. + +Reproduce from the source revision: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite trainer +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_trainer +``` + +Validate offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T193001Z-cece3e8f +``` diff --git a/benchmarks/results/foundation/20260905T193001Z-cece3e8f/junit.xml b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/junit.xml new file mode 100644 index 0000000..ea0883e --- /dev/null +++ b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T193001Z-cece3e8f/manifest.json b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/manifest.json new file mode 100644 index 0000000..0a6d35f --- /dev/null +++ b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/manifest.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "suite": "trainer", + "test_files": [ + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "test_trainer.py" + ], + "source_sha": "81acf0bf3b3e2ff5da437cdafe1b1c94169acfac", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "test_histograms.py": "bd0bdf6747b26d2050f5bba01d36536c14afb7f9a73e933a0bdad8e8bb6c51c7", + "histogram_oracle.py": "051ef6709a5274845f73d731c093dd9a432e73ac0a65343c7bb2fbad1eb21340", + "test_splits.py": "8fa1f4403412a5bb7d2fc4602ca4470c0047f5f587fb517860411ca1326397b6", + "split_oracle.py": "fc95859d6c9306af81d77ea31c799ea6cbd29bfec38f478012dddc873e7b7635", + "test_leaves.py": "c6b1a4827d5c3b381802f412438ddbea48e6eac996c0c511d2e9673230ba2d60", + "leaf_oracle.py": "8dc8c6132c3061691c95f1cbe424cbd5be9ba11273f2e37432b6c20855e83018", + "test_builder.py": "f7b9256c028c5e86471c5be21cc549f020ef42873e3cb26563c2cbe75d53299c", + "builder_oracle.py": "10eff9d1d590faf48c17b19dd1367c08e40be8f23070a9581a866c8cfde3d032", + "test_trainer.py": "19aea65d712e4b6c23a9704f2ad1fca66ae6cb4163a5c315d084632fe600ec2b" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_trainer" + ], + "gpu": "T4", + "timeout_s": 300, + "retries": 0, + "dataset": { + "generator": "numpy.default_rng", + "seed": 31, + "shape": [ + 256, + 4 + ], + "split": "smoke uses training data; no held-out quality claim" + }, + "run_id": "20260905T193001Z-cece3e8f", + "modal_image_id": "im-toFYy1pTudfW5L2RBzBhns" +} diff --git a/benchmarks/results/foundation/20260905T193001Z-cece3e8f/results.json b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/results.json new file mode 100644 index 0000000..b06a19a --- /dev/null +++ b/benchmarks/results/foundation/20260905T193001Z-cece3e8f/results.json @@ -0,0 +1,635 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 266817998848, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "openboost": "1.0.0rc1", + "cuda-core": "0.6.0", + "execnet": "2.1.2", + "pip": "23.3.2", + "coverage": "7.13.1", + "packaging": "25.0", + "joblib": "1.5.3", + "llvmlite": "0.46.0", + "scipy": "1.16.3", + "Pygments": "2.19.2", + "cuda-bindings": "13.1.1", + "iniconfig": "2.3.0", + "pytest": "9.0.2", + "pluggy": "1.6.0", + "numpy": "2.3.5", + "numba": "0.63.1", + "cupy-cuda12x": "13.6.0", + "pytest-xdist": "3.8.0", + "numba-cuda": "0.27.0", + "setuptools": "69.0.3", + "cuda-pathfinder": "1.4.0", + "pytest-cov": "7.0.0", + "fastrlock": "0.8.3", + "hyperframe": "6.1.0", + "cbor2": "5.7.0", + "attrs": "25.3.0", + "aiosignal": "1.3.2", + "yarl": "1.20.0", + "h2": "4.2.0", + "idna": "3.10", + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7", + "typing_extensions": "4.13.2", + "grpclib": "0.4.8", + "frozenlist": "1.6.0", + "multidict": "6.4.4", + "hpack": "4.1.0", + "certifi": "2025.4.26", + "protobuf": "6.31.1", + "propcache": "0.3.1" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "81acf0bf3b3e2ff5da437cdafe1b1c94169acfac", + "wheel_sha256": "8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_histograms.py", + "test_splits.py", + "test_leaves.py", + "test_builder.py", + "test_trainer.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "....... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\ntest_trainer.py::test_strict_extension_trainer\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_histograms.py::test_batch_histogram_device_oracle\n /usr/local/lib/python3.12/site-packages/numba/cpython/hashing.py:477: UserWarning: FNV hashing is not implemented in Numba. See PEP 456 https://www.python.org/dev/peps/pep-0456/ for rationale over not using FNV. Numba will continue to work, but hashes for built in types will be computed using siphash24. This will permit e.g. dictionaries to continue to behave as expected, however anything relying on the value of the hash opposed to hash as a derived property is likely to not work as expected.\n warnings.warn(msg)\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n7 passed, 22 warnings in 38.78s\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "batch_histograms": { + "device_arrays": true, + "cases": [ + { + "input_sha256": "eb00262be585e171ab37b32c1ea39a71c45d86b95a243c3760918664e2b968ff", + "seed": null, + "samples": 6, + "features": 2, + "slots": 4, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 16404 + }, + { + "input_sha256": "9b3d6fc9090a17c8e05ab26c7cba643004e7b80e99f2ee57f6c6f721c7244365", + "seed": 103, + "samples": 4097, + "features": 3, + "slots": 7, + "max_abs_errors": [ + 9.834766387939453e-07, + 1.2516975402832031e-06, + 0.0 + ], + "bytes": 43043 + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "seed": null, + "samples": 0, + "features": 2, + "slots": 3, + "max_abs_errors": [ + 0.0, + 0.0, + 0.0 + ], + "bytes": 12303 + } + ], + "legacy_download_wrappers_blocked": true, + "scope": "named host wrappers only; scalar validation synchronization allowed; no profiler trace" + }, + "batch_splits": { + "device_arrays": true, + "routed_child_oracle": true, + "cases": [ + { + "parameters": {}, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 65.05263157894737, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + -1 + ] + }, + { + "parameters": { + "reg_lambda": 0.0, + "min_child_weight": 0.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + 0, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + 2, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 83.16577540106952, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 1, + 1, + 1, + 1, + 1, + 1, + 2, + -1 + ] + }, + { + "parameters": { + "min_gain": 1000000.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + }, + { + "parameters": { + "min_child_weight": 20.0 + }, + "input_sha256": "c1ab5ceda0b0613a6a04bc2c60a96ac85810371aac778926727a78c6a3a27ce6", + "feature": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "threshold": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1 + ], + "gain": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "routed_ids": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1 + ] + } + ], + "exact_ties_and_gain_boundary": true, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim" + }, + "batch_leaves": { + "device_arrays": true, + "row_sum_oracle": true, + "bounded_changes_next_gradient": true, + "cases": [ + { + "input_sha256": "6798811eba51f86e0857c90969b3d6628bd88328a97dc9a5c1c40a879a65a8da", + "samples": 6, + "slots": 4, + "seed": null, + "counts": [ + 3, + 0, + 1, + 0 + ], + "grad": [ + -14.0, + 0.0, + -3.0, + 0.0 + ], + "hess": [ + 4.0, + 0.0, + 1.0, + 0.0 + ], + "values": [ + 2.799999952316284, + -0.0, + 1.5, + -0.0 + ], + "bounded": [ + 0.5, + -0.0, + 0.5, + -0.0 + ] + }, + { + "input_sha256": "4dc60aca864b164786f1008cdb7789c82778b78df569e1a92207c5209b3a9ccf", + "samples": 4097, + "slots": 7, + "seed": 109, + "counts": [ + 519, + 0, + 512, + 475, + 0, + 531, + 539 + ], + "grad": [ + -2.5, + 0.0, + 176.0, + 228.0, + 0.0, + 120.0, + 4.5 + ], + "hess": [ + 612.0, + 0.0, + 535.5, + 521.5, + 0.0, + 607.5, + 623.5 + ], + "values": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ], + "bounded": [ + 0.004078303463757038, + -0.0, + -0.32805219292640686, + -0.4363636374473572, + -0.0, + -0.19720624387264252, + -0.007205764763057232 + ] + }, + { + "input_sha256": "75c8fd04ad916aec3e3d5cb76a452b116b3d4d0912a0a485e9fb8e3d240e210c", + "samples": 0, + "slots": 3, + "seed": null, + "counts": [ + 0, + 0, + 0 + ], + "grad": [ + 0.0, + 0.0, + 0.0 + ], + "hess": [ + 0.0, + 0.0, + 0.0 + ], + "values": [ + -0.0, + -0.0, + -0.0 + ], + "bounded": [ + -0.0, + -0.0, + -0.0 + ] + } + ], + "two_rounds": [ + { + "raw": [ + 3.3600001335144043, + 3.3600001335144043 + ], + "second_gradient": [ + 0.7999999523162842, + -3.6000001430511475 + ] + }, + { + "raw": [ + 1.0, + 1.0 + ], + "second_gradient": [ + -1.5, + -10.5 + ] + } + ], + "scope": "GPU primitive composition; CPU trainer tested separately; not assembled GPU Booster" + }, + "levelwise_builder": { + "device_cache_survives_owner_release": true, + "compact_transfer_calls": 85, + "compact_transfer_bytes": 2380, + "two_channel_cases": [ + { + "samples": 16, + "seed": 127, + "clipped": false, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.376172378638702, + "crps": 0.5607174393426145 + }, + "cuda": { + "nll": 1.3761723780246782, + "crps": 0.5607174390134131 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 16, + "seed": 127, + "clipped": true, + "data_sha256": "0e528a11a3f672aa06b360d29bd0e9f27925aa60756452ffd14304990632c080", + "cpu": { + "nll": 1.4545325936069222, + "crps": 0.6108751652432844 + }, + "cuda": { + "nll": 1.4545325948769345, + "crps": 0.6108751656755558 + }, + "max_raw_error": 7.450580596923828e-09 + }, + { + "samples": 4097, + "seed": 127, + "clipped": false, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.339077446639963, + "crps": 0.5416246632667225 + }, + "cuda": { + "nll": 1.339077459597871, + "crps": 0.541624668350978 + }, + "max_raw_error": 1.564621925354004e-07 + }, + { + "samples": 4097, + "seed": 127, + "clipped": true, + "data_sha256": "27bc432d9b960ae7843fd98d5b33218e646d990c643e1f5067c8449af1df29e8", + "cpu": { + "nll": 1.5450022972468154, + "crps": 0.6447824943510968 + }, + "cuda": { + "nll": 1.5450022964562025, + "crps": 0.6447824952472991 + }, + "max_raw_error": 2.2351741790771484e-08 + } + ], + "cpu_load_prediction": true, + "scope": "direct GPU builder composition; GPU Booster.fit remains P5; named transfers only" + }, + "strict_extension_trainer": { + "adapter_cases": [ + { + "distribution": "normal", + "natural": false, + "target_sha256": "e53a62533eed353d90ace7744347822d8a31b9dc2ad4e93af74c6195a608f32a", + "max_raw_error": 1.1920928955078125e-07 + }, + { + "distribution": "normal", + "natural": true, + "target_sha256": "e53a62533eed353d90ace7744347822d8a31b9dc2ad4e93af74c6195a608f32a", + "max_raw_error": 1.1920928955078125e-07 + }, + { + "distribution": "poisson", + "natural": false, + "target_sha256": "90d27f84e5a24dfaa5d3c66768b05130e46842a9b3b3fd6b01dd35ec59f0af6c", + "max_raw_error": 5.960464477539063e-08 + }, + { + "distribution": "poisson", + "natural": true, + "target_sha256": "90d27f84e5a24dfaa5d3c66768b05130e46842a9b3b3fd6b01dd35ec59f0af6c", + "max_raw_error": 5.960464477539063e-08 + } + ], + "additional_invalid_statistics": 3, + "actual_fit": true, + "legacy_dispatch_blocked": true, + "rollback": true, + "cpu_load_prediction": true, + "cases": [ + { + "explicit_builder": false, + "fit_report": { + "requested_device": "cuda", + "actual_device": "cuda", + "binning_device": "cpu", + "initialization_device": "cpu", + "objective_device": "cuda", + "tree_device": "cuda", + "update_device": "cuda", + "eval_device": null, + "builder_path": "LevelWiseBuilder", + "fallback_reason": null, + "random_state": 7, + "tree_counts": { + "loc": 2, + "scale": 2 + }, + "timing_scope": "No performance timing collected", + "transfer_scope": "one-time binned/y/weights upload; compact tree finalization downloads; device-only defensive input copies and cache verification; scalar validation syncs; predict_raw uses CPU trees; no profiler proof" + }, + "max_raw_error": 1.1920928955078125e-07, + "cpu_crps": 0.5792920059096566, + "cuda_crps": 0.5792920073406618, + "cpu_nll": 1.462673544883728, + "cuda_nll": 1.462673544883728 + }, + { + "explicit_builder": true, + "fit_report": { + "requested_device": "cuda", + "actual_device": "cuda", + "binning_device": "cpu", + "initialization_device": "cpu", + "objective_device": "cuda", + "tree_device": "cuda", + "update_device": "cuda", + "eval_device": null, + "builder_path": "ExternalBuilder", + "fallback_reason": null, + "random_state": 7, + "tree_counts": { + "loc": 2, + "scale": 2 + }, + "timing_scope": "No performance timing collected", + "transfer_scope": "one-time binned/y/weights upload; compact tree finalization downloads; device-only defensive input copies and cache verification; scalar validation syncs; predict_raw uses CPU trees; no profiler proof" + }, + "max_raw_error": 5.960464477539063e-08, + "cpu_crps": 0.5792920059096566, + "cuda_crps": 0.5792920124632472, + "cpu_nll": 1.462673544883728, + "cuda_nll": 1.462673544883728 + } + ], + "compact_transfer_calls": 40, + "compact_transfer_bytes": 1120, + "data_sha256": "2b189618a4b1b0665498afa0ecd6abbe11e1d0231706995d747b65ad783ced6e", + "samples": 257, + "seed": 137, + "nsys_available": false, + "profiler_trace_collected": false, + "scope": "named transfer wrappers only; scalar syncs and device defensive copies allowed" + } + }, + "remote_function_wall_s": 42.878989436, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md new file mode 100644 index 0000000..dab789a --- /dev/null +++ b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md @@ -0,0 +1,86 @@ +# Fixed-slot growth: modest improvement, performance budget still fails + +Clean measurement source `a5bf26d3d54374e4c524375a02294f0c64abbc1c`, implementation +`81acf0b`. Core wheel `8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077`. +T4: **3 passed / 0 skipped**, all 12 cells completed without fallback. This is +complete evidence, not a passing performance gate. Same P2 Housing hash/splits, +seeds 0/1/2, 30 rounds, depth 3, learning rate .05 and 64 bins as original P7. + +| Strategy | Seed 0 warm fit s | Seed 1 | Seed 2 | Cross-seed median s | +|---|---:|---:|---:|---:| +| Legacy CPU | 2.3427 | 2.3476 | 2.3655 | 2.3476 | +| Legacy CUDA | .1444 | .1476 | .1449 | .1449 | +| Default experimental CUDA | 1.8680 | 1.8537 | 1.8722 | 1.8680 | +| Independent A+B+C CUDA | 1.8870 | 1.9187 | 1.8895 | 1.8895 | + +Against the [original P7 matrix](../20260905T183820Z-3c245f2d/README.md), recorded +default warm fit median decreased from 2.078694 to 1.868019 s (10.13%). Legacy +CUDA also decreased from .149556 to .144938 s. The ratio against each run's +paired legacy reference changes from 13.899x to **12.888x** (7.27% lower). +Per-seed current ratios are 12.935, 12.555 and 12.917. These are three-split +observations across separate T4 runs, not a significance claim or attribution +of every percentage point to code. Keep the small fixed-shape implementation; +it preserves checks and shows consistent raw reductions, but does not solve G4. + +Process-first default fits: 19.488, 19.419, 19.369 s. Default prediction medians: +.7411, .7536, .7456 s versus legacy CUDA .0699, .0701, .0720 s. The experimental +API still predicts on CPU. No prediction speed improvement is claimed. + +| Seed | Default NLL | CRPS | Coverage90 | Independent A+B+C NLL | CRPS | Coverage90 | +|---|---:|---:|---:|---:|---:|---:| +| 0 | 1.094474 | .398591 | .967781 | 1.483626 | .599585 | .923934 | +| 1 | 1.113435 | .410067 | .963905 | 1.511263 | .618114 | .915698 | +| 2 | 1.096392 | .403820 | .964390 | 1.494126 | .606814 | .922481 | + +Every default fit/seed passes the original quality thresholds, and same-run +legacy matches frozen P2. Nominal 90% coverage remains overconservative. The +independent Fisher/bounded/scheduled algorithm still has worse proper scores; +closer nominal coverage alone is not a win. No seed, configuration or tolerance +was changed. Strict CUDA eval/callbacks remain outside this comparison. + +## Remaining overhead + +Isolated seed-0 diagnostic wall time is 2.0466 s: tree/session boundary 1.9099 s, +nested LevelWiseBuilder 1.3641 s, objective boundary .0866 s. The preceding +[isolated profile](../20260905T184856Z-dcd49569/README.md) recorded builder 1.5880 s +and tree/session 2.1226 s. Boundary timers are synchronized, nested and include +instrumentation overhead; do not use them as uninstrumented production timings. +Removing split-array compaction addresses only part of the overhead. Remaining +candidates include repeated validation/synchronization and the separate cached +prediction traversal; none has been removed or shown to deliver a speedup here. + +Four timed fits per cell precede an isolated fifth host-profile fit and sixth +memory-only fit for CUDA (CPU memory phase is inapplicable). The original manifest's +profile prose predates this separation; the hashed worker and each memory scope +record actual execution. `d816954` clarifies future manifests without rewriting +this artifact. Timed fits use fresh process/Numba/CuPy caches, exclude imports, +data loading and context startup, include binning/gradients/transfers/JIT, and +leave the driver cache intact. They are not machine-cold timings. + +Sampled device-wide memory is a lower bound including contexts/caches, not an +exact per-fit peak; named transfer wrappers are not a full CUDA trace. nsys is +unavailable. Function 294.95 s / pytest 291.04 s are not billing records. One +T4, requested 2 CPU / 8192 MiB, threads 2, 1800-second limit, no retries. Full +OS/Python/package/GPU/driver/data/hash/command provenance is retained in JSON. + +[Real CUDA correctness](../20260905T193001Z-cece3e8f/README.md): 7 passed / 0 skipped, +including Normal/Poisson and CPU/CUDA task metrics. [Independent CPU wheels](../p7-fixed-slot-cpu-a5bf26d/README.md): +7 passed plus six exact predictions after plugin removal. Both verify this same +core wheel. No validation, persistence, sampling or feature capability weakened. + +Reproduce from the measurement source revision: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite value +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_value +``` + +Validate offline: + +```sh +uv run --no-sync python -m benchmarks.foundation.runner benchmarks/results/foundation/20260905T193308Z-5ebd75ab +``` + +G4's 1.2 fit-ratio budget still fails. Keep the bounded experimental research API; +do not replace the legacy CUDA path or claim general speed/cost superiority. +External adoption, exact per-fit GPU peak and a complete CUDA trace remain open. diff --git a/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/junit.xml b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/junit.xml new file mode 100644 index 0000000..65d86ca --- /dev/null +++ b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/manifest.json b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/manifest.json new file mode 100644 index 0000000..f855c49 --- /dev/null +++ b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/manifest.json @@ -0,0 +1,119 @@ +{ + "schema_version": 1, + "suite": "value", + "test_files": [ + "test_smoke.py", + "test_value.py" + ], + "source_sha": "a5bf26d3d54374e4c524375a02294f0c64abbc1c", + "source_dirty": false, + "wheel": "openboost-1.0.0rc1-py3-none-any.whl", + "wheel_sha256": "8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077", + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "files": { + "test_smoke.py": "1e1eddf1f7e6ca4f4a744f426268e23da806241d0a32be9a83c720e12d28ea8d", + "conftest.py": "74551355ebf2a700cf28ebcf41e9826540f23309b1d0eb41e13a624e9924703f", + "pytest.ini": "3497504c3be101997137e28318681bdf533d7eb0f6d7b31e7206656b6308422d", + "requirements.txt": "9b2c6b9fe476b5c728deacf558d726437fc16e64998376e78f3676c41cbf0d4a", + "dataset.py": "cee302d02c1f82e54c19693fdbff8a7671955f816cccb6fc5902860414109e2a", + "baseline_worker.py": "3abb07c372d2fae3ffc8973ff1b1220d207302b9f04282514762eb907e363e88", + "housing.json": "d511bf1b61c6e515598a91cc468b9c860dcde60f9e1e7a3e2570e9d8e8c3bc5b", + "test_value.py": "81f6524faa7f20a7f5ee6791fc0cbd3d75119dd021a1a9a28a53e1105ec6b060", + "value_worker.py": "bbcacfd9e151acc67338241a18a6be6df9aabc9640d0245899625e1a39104e1f", + "value_protocol.py": "8d560b54c53e8b00ca37d91aadb55215776e628406bdd201d463a3464a40f332", + "p2_baseline.json": "1d024133ecd1975b7854eb7601b3a3a571981613aae8dd785dc98181ace78818", + "cal_housing.tgz": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681" + }, + "base_image": "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + "python": "3.12", + "uv_version": "0.12.1", + "command": [ + "uv", + "run", + "--no-sync", + "modal", + "run", + "benchmarks/foundation/modal_app.py::foundation_value" + ], + "gpu": "T4", + "timeout_s": 1800, + "retries": 0, + "dataset": { + "name": "California Housing", + "source_url": "https://ndownloader.figshare.com/files/5976036", + "archive_sha256": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681", + "member": "CaliforniaHousing/cal_housing.data", + "features": [ + "MedInc", + "HouseAge", + "AveRooms", + "AveBedrms", + "Population", + "AveOccup", + "Latitude", + "Longitude" + ], + "shape": [ + 20640, + 8 + ], + "target_unit": "100,000 USD", + "arrays_sha256": "34fc72e53a6329a89a9ae792e92f37853cea98adfbc6feea28cc08dc06f68ca1", + "dtype": "little-endian float32", + "split": "numpy.default_rng(seed), permutation, 60/20/20", + "split_sha256": { + "0": [ + "7d07544c6c3fddd72d0cfc8d0aa0c19cf00250f1890420bdb10bd80efb0c9c04", + "5f6d9931d5a5239c7933da9bb62df2c631e79017658f185742db94f139be358a", + "877744733fe0213e716ca084333f39385c30654169f117f772c53a6af8bcff37" + ], + "1": [ + "99578d3ef329ef275fd41e3834df7dab763525f9374db203b6f748b783eed05a", + "a161a60cd85d1ec54cbfd590f6befdb58b1a9a695c635bfbb562b4064d265caf", + "039c4fc07f6596a3cfdf73cb55820de4baa13f93251ede4c215796f283d46256" + ], + "2": [ + "51ab6cbe0019b5bea9f9da8a81b3fafa874d8c992062f65ee8d1b09a3b7898a1", + "14509d20c7d87948ac6ea9060c476995583dd130af0ff928a9c24709e5ecf9d4", + "ff6426d360c75b637dd866cd73d9fd1b9fcf310a886dc05fbac0f80dcf256ec8" + ] + } + }, + "extension_wheels": { + "openboost_example_normal_fisher-0.2.0-py3-none-any.whl": "47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b", + "openboost_example_bounded_leaves-0.2.0-py3-none-any.whl": "dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3" + }, + "extension_sources": { + "examples/extensions/normal_fisher/README.md": "78a5142eaf92a81df9c5908861a1ce66e5ae8b03bf753e0b84a834c5f2a5d0eb", + "examples/extensions/normal_fisher/pyproject.toml": "ab70495fafa430dfb1377a873cbb821c9484c9008cea26575fa3c3fa2a91e40d", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "cabafae48d9ad251eea62230b1d38ba7407fbb113dec116b3e840c05e032afd3", + "examples/extensions/normal_fisher/tests/test_normal.py": "6dcb48f9434879b054fe9a9e8b6fca5f4ba01782d8c45ed4c566a8306ee867c3", + "examples/extensions/bounded_leaves/README.md": "749f68fafb9e8cb5911c16aa79ab7a82593599e5c196c20be3ec9923172144e9", + "examples/extensions/bounded_leaves/pyproject.toml": "8731c3fa03f03341fcc987b449c04658c5bde3e7a4d24772e3bc2199ceb55eac", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "d53927c63c71ea3cf37083e0d71d11aff58941dbed1df430838c2d6b2912efff", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "41339c49f146181811dd0680d2b1abc563448ce35b4a79e8d4f0559c5d949baf" + }, + "value_protocol": { + "seeds": [ + 0, + 1, + 2 + ], + "strategies": [ + "legacy_cpu", + "legacy_cuda", + "experimental_cuda", + "extensions_cuda" + ], + "mode": "resident", + "repetitions": 4, + "warm_fit_budget_ratio": 1.2, + "extension_bound": 0.5, + "extension_schedule_tau": 1, + "cache_policy": "fresh process and NUMBA/CUPY cache directories per cell; device context startup/imports excluded; driver cache not cleared", + "profile": "separate fifth warm fit, cProfile host attribution and 5 ms sampled device-wide memory; not a kernel trace", + "cost": "T4 seconds only; billing unavailable" + }, + "run_id": "20260905T193308Z-5ebd75ab", + "modal_image_id": "im-SkobQid810UeBbiZWEoHe7" +} diff --git a/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/results.json b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/results.json new file mode 100644 index 0000000..b69e76e --- /dev/null +++ b/benchmarks/results/foundation/20260905T193308Z-5ebd75ab/results.json @@ -0,0 +1,7352 @@ +{ + "environment": { + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "python": "3.12.1 (main, Jan 8 2024, 04:46:10) [Clang 17.0.6 ]", + "cpu": "x86_64", + "visible_cpu_count": 18, + "requested_cpu": 2, + "requested_memory_mib": 8192, + "host_ram_bytes": 266818023424, + "threads": { + "OMP_NUM_THREADS": "2", + "NUMBA_NUM_THREADS": "2", + "OPENBLAS_NUM_THREADS": "2" + }, + "packages": { + "openboost-example-normal-fisher": "0.2.0", + "cuda-pathfinder": "1.4.0", + "pytest-xdist": "3.8.0", + "cuda-core": "0.6.0", + "setuptools": "69.0.3", + "cupy-cuda12x": "13.6.0", + "pytest-cov": "7.0.0", + "llvmlite": "0.46.0", + "pluggy": "1.6.0", + "fastrlock": "0.8.3", + "numpy": "2.3.5", + "openboost": "1.0.0rc1", + "numba": "0.63.1", + "pytest": "9.0.2", + "pip": "23.3.2", + "numba-cuda": "0.27.0", + "Pygments": "2.19.2", + "cuda-bindings": "13.1.1", + "packaging": "25.0", + "iniconfig": "2.3.0", + "openboost-example-bounded-leaves": "0.2.0", + "execnet": "2.1.2", + "joblib": "1.5.3", + "scipy": "1.16.3", + "coverage": "7.13.1", + "propcache": "0.3.1", + "multidict": "6.4.4", + "hpack": "4.1.0", + "protobuf": "6.31.1", + "typing_extensions": "4.13.2", + "aiohttp": "3.12.7", + "grpclib": "0.4.8", + "cbor2": "5.7.0", + "h2": "4.2.0", + "aiosignal": "1.3.2", + "frozenlist": "1.6.0", + "aiohappyeyeballs": "2.6.1", + "hyperframe": "6.1.0", + "attrs": "25.3.0", + "yarl": "1.20.0", + "certifi": "2025.4.26", + "idna": "3.10" + }, + "cpu_model": "unknown", + "cuda_available": true, + "gpu_name": "Tesla T4", + "cuda_runtime": 12090, + "cuda_driver": 13000, + "nvidia_smi": "Tesla T4, 580.95.05, 15360 MiB" + }, + "source_sha": "a5bf26d3d54374e4c524375a02294f0c64abbc1c", + "wheel_sha256": "8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077", + "argv": [ + "/usr/local/bin/python", + "-m", + "pytest", + "-c", + "pytest.ini", + "test_smoke.py", + "test_value.py", + "--junitxml=junit.xml" + ], + "timed_out": false, + "returncode": 0, + "stdout": "... [100%]\n=============================== warnings summary ===============================\ntest_smoke.py: 12 warnings\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 1 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 4 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 8 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\ntest_smoke.py::test_normal_gpu_fit\n /usr/local/lib/python3.12/site-packages/numba_cuda/numba/cuda/dispatcher.py:716: NumbaPerformanceWarning: Grid size 2 will likely result in GPU under-utilization due to low occupancy.\n warn(errors.NumbaPerformanceWarning(msg))\n\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n3 passed, 17 warnings in 291.04s (0:04:51)\n", + "stderr": "", + "junit": "", + "checks": { + "installed_files_verified": 46, + "installed_module": "/usr/local/lib/python3.12/site-packages/openboost/__init__.py", + "interop": true, + "dataset_sha256": "a6ebf89ed613d8f792084b55e7f6a3ce93360add808cd1729aa8e2842cce18ec", + "nll": 1.3506839275360107, + "native_tree_calls": 4, + "device_objective_calls": 2, + "value_cells": [ + { + "strategy": "legacy_cpu", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 4.277136346000001, + "predict_s": 0.68453023, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + }, + { + "phase": "warm_1", + "fit_s": 2.384892239000001, + "predict_s": 0.677785999000001, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + }, + { + "phase": "warm_2", + "fit_s": 2.342717186999998, + "predict_s": 0.6748511969999988, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + }, + { + "phase": "warm_3", + "fit_s": 2.3398985619999983, + "predict_s": 0.6758734390000001, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.3522424629999996, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.013281731999985169 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.5702000000000004e-05, + "cumulative_s": 2.3521666850000003 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.2063e-05, + "cumulative_s": 2.3520963150000003 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.0024411930000000004, + "cumulative_s": 2.352063534 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00031531500000000004, + "cumulative_s": 1.9948652770000002 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 6.0719000000000006e-05, + "cumulative_s": 1.994449415 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00011106500000000001, + "cumulative_s": 1.9943886960000001 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 1.991590516, + "cumulative_s": 1.9941351420000002 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0010808340000000001, + "cumulative_s": 0.31835398800000003 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.004804223000000001, + "cumulative_s": 0.31586944100000003 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000743335, + "cumulative_s": 0.09752616900000001 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.059129969000000004, + "cumulative_s": 0.09640348400000001 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000505641, + "cumulative_s": 0.082773358 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.073878339, + "cumulative_s": 0.081809142 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.8259e-05, + "cumulative_s": 0.07180344100000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000447557, + "cumulative_s": 0.071737516 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.0004e-05, + "cumulative_s": 0.052140353 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.558900000000001e-05, + "cumulative_s": 0.051966287 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.003988393000000001, + "cumulative_s": 0.045707274000000006 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.6671000000000003e-05, + "cumulative_s": 0.04526181 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.0202e-05, + "cumulative_s": 0.04505382 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00022693500000000002, + "cumulative_s": 0.045009713 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 9.070600000000001e-05, + "cumulative_s": 0.044280765 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.035935139000000005, + "cumulative_s": 0.044062399 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.7637e-05, + "cumulative_s": 0.043562947000000005 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.1528e-05, + "cumulative_s": 0.043187166000000006 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000196796, + "cumulative_s": 0.043098749000000006 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.0445e-05, + "cumulative_s": 0.038479353 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.1462000000000001e-05, + "cumulative_s": 0.03846443 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00041115000000000005, + "cumulative_s": 0.0384446 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.003362981, + "cumulative_s": 0.037864533000000006 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 3823, + "calls": 3839, + "self_s": 0.015786085000000002, + "cumulative_s": 0.036590813 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00028173900000000003, + "cumulative_s": 0.035824104 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000371846, + "cumulative_s": 0.035465133 + }, + { + "file": "_distributions.py", + "line": 211, + "function": "natural_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004110608, + "cumulative_s": 0.033540556 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.025147469000000002, + "cumulative_s": 0.031900937000000004 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.027090759000000002, + "cumulative_s": 0.028139569000000003 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0017475600000000002, + "cumulative_s": 0.027393242 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 185, + "calls": 196, + "self_s": 0.000759724, + "cumulative_s": 0.025886232000000002 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.4202e-05, + "cumulative_s": 0.025653560000000002 + }, + { + "file": "_distributions.py", + "line": 393, + "function": "nll_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0019676760000000002, + "cumulative_s": 0.02537897 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000371846, + "cumulative_s": 0.035465133 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000182167, + "cumulative_s": 0.000278242 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.342717186999998, + "warm_predict_median_s": 0.6758734390000001 + }, + { + "strategy": "legacy_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 3.202888213999998, + "predict_s": 0.1906499609999983, + "metrics": { + "nll": 1.0944737577325385, + "crps": 0.39859095196987343, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "a8dd431585f9ef84b0254ca558800ab3ba812d9e0289235dc95ae4c281bdf6f8", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 0.14442114699999564, + "predict_s": 0.06988878099999596, + "metrics": { + "nll": 1.0944737575543853, + "crps": 0.39859095168896175, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "e13e91d316e93e07983d8d1ab008c90f6051769ea24b60bef47b07aee16e708f", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 0.14504427399999997, + "predict_s": 0.06969488200000029, + "metrics": { + "nll": 1.094473759071128, + "crps": 0.3985909511383087, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "a302aef54d5c44595f9d71e4ac59d6c3205fb1208b5ef05a236283a442543c6a", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 0.141903868, + "predict_s": 0.07008707299999628, + "metrics": { + "nll": 1.0944737635239177, + "crps": 0.39859095407580325, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "e703aa88627e8c5c5aa70ac85bc2df27aa0829bc1e86b1db350e9c6101006f54", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.24747026500000402, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.020289052999991952 + }, + "legacy_native_tree": { + "calls": 60, + "inclusive_s": 0.17953680200001543 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 30, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.0031000000000003e-05, + "cumulative_s": 0.24737915100000002 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.4511e-05, + "cumulative_s": 0.24730126200000002 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000813722, + "cumulative_s": 0.24710627500000001 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 90, + "self_s": 0.0006734870000000001, + "cumulative_s": 0.202529695 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000495234, + "cumulative_s": 0.177077534 + }, + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.000917398, + "cumulative_s": 0.16883733 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0030062960000000003, + "cumulative_s": 0.16791993200000002 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0031832680000000004, + "cumulative_s": 0.164270296 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.02319732, + "cumulative_s": 0.151478087 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.027585707, + "cumulative_s": 0.10041111700000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.491e-05, + "cumulative_s": 0.06952240700000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000529938, + "cumulative_s": 0.069460768 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.001545347, + "cumulative_s": 0.055889495000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.7803e-05, + "cumulative_s": 0.049115563 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.0410000000000006e-05, + "cumulative_s": 0.049000706000000005 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.020551339000000002, + "cumulative_s": 0.046835538 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.4697000000000002e-05, + "cumulative_s": 0.043241638000000006 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.2864e-05, + "cumulative_s": 0.043124798000000006 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00017681900000000002, + "cumulative_s": 0.043081635 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.9686000000000003e-05, + "cumulative_s": 0.042000546 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.9648000000000002e-05, + "cumulative_s": 0.041763792 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000133966, + "cumulative_s": 0.041689534 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4760, + "calls": 4760, + "self_s": 0.012747312, + "cumulative_s": 0.039009735000000004 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 6.012e-06, + "cumulative_s": 0.034858405 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 9.544e-06, + "cumulative_s": 0.034848073 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00029028500000000004, + "cumulative_s": 0.034830822000000004 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2331, + "calls": 2331, + "self_s": 0.0022149640000000003, + "cumulative_s": 0.032940081 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 130, + "calls": 190, + "self_s": 0.000359458, + "cumulative_s": 0.032749762 + }, + { + "file": "threading.py", + "line": 1115, + "function": "join", + "primitive_calls": 3, + "calls": 3, + "self_s": 4.5358e-05, + "cumulative_s": 0.032712284 + }, + { + "file": "threading.py", + "line": 1153, + "function": "_wait_for_tstate_lock", + "primitive_calls": 40, + "calls": 40, + "self_s": 2.6428000000000002e-05, + "cumulative_s": 0.032646158 + }, + { + "file": "devices.py", + "line": 147, + "function": "_get_or_create_context_uncached", + "primitive_calls": 2015, + "calls": 2015, + "self_s": 0.008423799000000001, + "cumulative_s": 0.030019018 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 1715, + "calls": 1715, + "self_s": 0.000803167, + "cumulative_s": 0.028988601000000003 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0014561630000000001, + "cumulative_s": 0.026931497000000002 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.8686e-05, + "cumulative_s": 0.022872054000000003 + }, + { + "file": "_objectives.py", + "line": 117, + "function": "init_raw", + "primitive_calls": 1, + "calls": 1, + "self_s": 7.726e-06, + "cumulative_s": 0.021409798 + }, + { + "file": "_distributions.py", + "line": 384, + "function": "init_params", + "primitive_calls": 1, + "calls": 1, + "self_s": 1.4725e-05, + "cumulative_s": 0.021400966 + }, + { + "file": "fromnumeric.py", + "line": 3869, + "function": "std", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.6402e-05, + "cumulative_s": 0.021384874 + }, + { + "file": "_methods.py", + "line": 220, + "function": "_std", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.5137e-05, + "cumulative_s": 0.021143968000000003 + }, + { + "file": "driver.py", + "line": 466, + "function": "__enter__", + "primitive_calls": 2631, + "calls": 2631, + "self_s": 0.008812318000000001, + "cumulative_s": 0.021054438 + }, + { + "file": "_methods.py", + "line": 150, + "function": "_var", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.00012371500000000002, + "cumulative_s": 0.02078489 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000495234, + "cumulative_s": 0.177077534 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000134883, + "cumulative_s": 0.019125464000000002 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.001608445, + "cumulative_s": 0.008976837000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 0.14442114699999564, + "warm_predict_median_s": 0.06988878099999596 + }, + { + "strategy": "experimental_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 19.48809497, + "predict_s": 0.742599693999999, + "metrics": { + "nll": 1.0944737448877884, + "crps": 0.39859095476503065, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "31a06edb85908a871ab810ee4d91e089ee5ac3eaf9c290060819eb719413ca12", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 1.8680187150000052, + "predict_s": 0.7428813990000052, + "metrics": { + "nll": 1.0944737695327262, + "crps": 0.3985909631329879, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "ddef658b12fd306c86aa9ba2b97f8f3a7e557d0f3f0eb0ccf908c486a96f0f3d", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 1.8892137269999978, + "predict_s": 0.7404956039999959, + "metrics": { + "nll": 1.0944737414342998, + "crps": 0.3985909535522184, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "d24d9e061e9ea60e4f070a9ba343e18be477180f2dfe88ae525f152becb00717", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 1.8568208040000016, + "predict_s": 0.7411200060000027, + "metrics": { + "nll": 1.0944737445557418, + "crps": 0.3985909540772088, + "coverage90": 0.967781007751938 + }, + "fallback_warnings": [], + "prediction_sha256": "8342eb8121da792c99e869a4912fb769507b51258e8cf4116daade14ad5dc5e2", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.046606972999996, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.031273137999974665 + }, + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.08658873699994274 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.3640738420000105 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 1.9099124579999938 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 2097152, + "samples": 368, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000186373, + "cumulative_s": 2.046458521 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 5.6695000000000005e-05, + "cumulative_s": 2.045981323 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.005380132, + "cumulative_s": 2.0458534210000003 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 180, + "self_s": 0.0033781920000000003, + "cumulative_s": 2.000712206 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.400190062, + "cumulative_s": 1.9054254460000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.056350196000000005, + "cumulative_s": 1.359191129 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.601774514, + "cumulative_s": 0.6720656610000001 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.10008964100000001, + "cumulative_s": 0.205806013 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.098750158, + "cumulative_s": 0.19596627700000002 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.026125342000000003, + "cumulative_s": 0.150638659 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.0037945020000000004, + "cumulative_s": 0.14322752500000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.136576428, + "cumulative_s": 0.136576428 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.125615431, + "cumulative_s": 0.125615431 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002792105, + "cumulative_s": 0.10438624 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.636500000000001e-05, + "cumulative_s": 0.08472668300000001 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0068805540000000005, + "cumulative_s": 0.08466304300000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00043879600000000003, + "cumulative_s": 0.084648473 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.030666904000000002, + "cumulative_s": 0.062262120000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.8748e-05, + "cumulative_s": 0.059576015 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00011260500000000001, + "cumulative_s": 0.05941789 + }, + { + "file": "search.py", + "line": 177, + "function": "where", + "primitive_calls": 1320, + "calls": 1320, + "self_s": 0.027702306000000003, + "cumulative_s": 0.05612877 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.7129e-05, + "cumulative_s": 0.053115539 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.9423000000000003e-05, + "cumulative_s": 0.052944519 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00028284400000000003, + "cumulative_s": 0.052878884 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.0403000000000004e-05, + "cumulative_s": 0.05120473800000001 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.1413e-05, + "cumulative_s": 0.051085612 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00030851800000000003, + "cumulative_s": 0.051050841000000007 + }, + { + "file": "from_data.py", + "line": 56, + "function": "asarray", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.00027962500000000003, + "cumulative_s": 0.044245493000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.017874191, + "cumulative_s": 0.043965868000000005 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.0468e-05, + "cumulative_s": 0.042686065 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.2882000000000001e-05, + "cumulative_s": 0.042671642 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000303632, + "cumulative_s": 0.042647987000000005 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.015011691, + "cumulative_s": 0.041072706 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.001438854, + "cumulative_s": 0.040168605 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018919588, + "cumulative_s": 0.038371279 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009391130000000001, + "cumulative_s": 0.038220169000000005 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.020969292, + "cumulative_s": 0.037928785 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00029354900000000005, + "cumulative_s": 0.035961145 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1500, + "calls": 1500, + "self_s": 0.032557082, + "cumulative_s": 0.032598688 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000182119, + "cumulative_s": 0.029773089000000003 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.400190062, + "cumulative_s": 1.9054254460000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.056350196000000005, + "cumulative_s": 1.359191129 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0068805540000000005, + "cumulative_s": 0.08466304300000001 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000182119, + "cumulative_s": 0.029773089000000003 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00028328200000000004, + "cumulative_s": 0.0068574460000000005 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 1.8680187150000052, + "warm_predict_median_s": 0.7411200060000027 + }, + { + "strategy": "extensions_cuda", + "seed": 0, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 20.313699401000008, + "predict_s": 0.7526009219999992, + "metrics": { + "nll": 1.483626393216994, + "crps": 0.5995845150144478, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "aa2af3f5d77f4f327f9c0bffb38bf8b7a8bf1f63860e090eee1e2fb7fdabd3b9", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 1.886970594999994, + "predict_s": 0.751930612999999, + "metrics": { + "nll": 1.4836264019257186, + "crps": 0.5995845166782734, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "2e2b54228958c032c56b761b93a5258897cd3d56489da5708fe0cd98f0248f9a", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 1.8895158009999875, + "predict_s": 0.7522788120000001, + "metrics": { + "nll": 1.4836264019257186, + "crps": 0.5995845166782734, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "2e2b54228958c032c56b761b93a5258897cd3d56489da5708fe0cd98f0248f9a", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 1.884154401999993, + "predict_s": 0.7636482969999889, + "metrics": { + "nll": 1.4836263997432595, + "crps": 0.5995845148900152, + "coverage90": 0.9239341085271318 + }, + "fallback_warnings": [], + "prediction_sha256": "503d86ce2fed87695ffdb93c18902e908775b3d942e3477bb17f07c663c077c9", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.0163288509999973, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.10804880499998148 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.3352666800000037 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 1.865307604000023 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 371, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 2044928 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000166792, + "cumulative_s": 2.016177897 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 2.489e-05, + "cumulative_s": 2.015683645 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.004773100000000001, + "cumulative_s": 2.015656867 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 150, + "self_s": 0.0029556760000000004, + "cumulative_s": 1.9773348720000001 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.39063861600000005, + "cumulative_s": 1.8609298680000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.054175349000000005, + "cumulative_s": 1.3305776930000002 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.5990896960000001, + "cumulative_s": 0.666773817 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09673364500000001, + "cumulative_s": 0.19830887000000003 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09584880200000001, + "cumulative_s": 0.188954649 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.02501808, + "cumulative_s": 0.147304348 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.0036840880000000003, + "cumulative_s": 0.13919624800000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.134848422, + "cumulative_s": 0.134848422 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.134377899, + "cumulative_s": 0.134377899 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006788054000000001, + "cumulative_s": 0.10614337800000001 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002650987, + "cumulative_s": 0.09996379000000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.6359e-05, + "cumulative_s": 0.07442770500000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000522965, + "cumulative_s": 0.074358108 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004580993, + "cumulative_s": 0.061373845 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029325839000000003, + "cumulative_s": 0.059762936 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.0016859940000000001, + "cumulative_s": 0.054958731000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.8025e-05, + "cumulative_s": 0.054209552 + }, + { + "file": "search.py", + "line": 177, + "function": "where", + "primitive_calls": 1320, + "calls": 1320, + "self_s": 0.026801357, + "cumulative_s": 0.054143302000000004 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.202900000000001e-05, + "cumulative_s": 0.054076148000000004 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.2647000000000002e-05, + "cumulative_s": 0.046643368000000004 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.6477e-05, + "cumulative_s": 0.046470575 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000256849, + "cumulative_s": 0.04641457 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.2980000000000005e-05, + "cumulative_s": 0.045112822000000004 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.9579e-05, + "cumulative_s": 0.044797518 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000170275, + "cumulative_s": 0.044707524000000005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 300, + "calls": 309, + "self_s": 0.002021663, + "cumulative_s": 0.044526219000000006 + }, + { + "file": "_function_base_impl.py", + "line": 434, + "function": "average", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000150578, + "cumulative_s": 0.044423828000000005 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.01003977, + "cumulative_s": 0.04129832 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000317548, + "cumulative_s": 0.039055545000000004 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.008223292, + "cumulative_s": 0.037675184 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.013505665, + "cumulative_s": 0.037358358 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.0360000000000006e-06, + "cumulative_s": 0.037308475 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.896e-06, + "cumulative_s": 0.037300780000000006 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000319779, + "cumulative_s": 0.037286632 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018342813, + "cumulative_s": 0.03678801 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.008969608, + "cumulative_s": 0.036658755 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.39063861600000005, + "cumulative_s": 1.8609298680000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.054175349000000005, + "cumulative_s": 1.3305776930000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006788054000000001, + "cumulative_s": 0.10614337800000001 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000252933, + "cumulative_s": 0.006420105000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 1.886970594999994, + "warm_predict_median_s": 0.7522788120000001 + }, + { + "strategy": "legacy_cpu", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 4.372001378000007, + "predict_s": 0.6771052120000007, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + }, + { + "phase": "warm_1", + "fit_s": 2.3495748419999956, + "predict_s": 0.685733349000003, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + }, + { + "phase": "warm_2", + "fit_s": 2.347555104999998, + "predict_s": 0.6900769020000013, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + }, + { + "phase": "warm_3", + "fit_s": 2.333803973000002, + "predict_s": 0.683446540999995, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.3525457779999925, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.015389065999926288 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.4991000000000004e-05, + "cumulative_s": 2.352463008 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.6719000000000005e-05, + "cumulative_s": 2.352400422 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.0028001280000000003, + "cumulative_s": 2.3523527630000003 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00027646500000000004, + "cumulative_s": 1.989039752 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 6.670000000000001e-05, + "cumulative_s": 1.9886602500000001 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00012729, + "cumulative_s": 1.98859355 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 1.985801291, + "cumulative_s": 1.988344574 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0011494300000000001, + "cumulative_s": 0.32053502100000003 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.005110268, + "cumulative_s": 0.318063001 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0007187910000000001, + "cumulative_s": 0.09667830200000001 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.057833920000000004, + "cumulative_s": 0.095575958 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0004983780000000001, + "cumulative_s": 0.08271541 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.073916825, + "cumulative_s": 0.081725346 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.270700000000001e-05, + "cumulative_s": 0.075259907 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000533729, + "cumulative_s": 0.07517401900000001 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.9267e-05, + "cumulative_s": 0.054573737000000004 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.7773e-05, + "cumulative_s": 0.054404147 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.5709e-05, + "cumulative_s": 0.047480398 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.3421e-05, + "cumulative_s": 0.047310187000000004 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000329842, + "cumulative_s": 0.047262162 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.004257110000000001, + "cumulative_s": 0.047238568 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.931e-05, + "cumulative_s": 0.045628672 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.9100000000000003e-05, + "cumulative_s": 0.045316785000000005 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000161538, + "cumulative_s": 0.045204173 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00010723300000000001, + "cumulative_s": 0.044725931000000003 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.036368147, + "cumulative_s": 0.044493724000000005 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00032837800000000005, + "cumulative_s": 0.03935006 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.003506709, + "cumulative_s": 0.039188182 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.552e-06, + "cumulative_s": 0.039048719 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.0261e-05, + "cumulative_s": 0.039041154 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00037215, + "cumulative_s": 0.039025435000000004 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000319341, + "cumulative_s": 0.0389525 + }, + { + "file": "_distributions.py", + "line": 211, + "function": "natural_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00444645, + "cumulative_s": 0.037044148 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.026381504, + "cumulative_s": 0.032981586 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.026985289000000003, + "cumulative_s": 0.028171342000000002 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 187, + "calls": 196, + "self_s": 0.00084062, + "cumulative_s": 0.027956232 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.001812047, + "cumulative_s": 0.027719334 + }, + { + "file": "_distributions.py", + "line": 393, + "function": "nll_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.0022303310000000003, + "cumulative_s": 0.027559756 + }, + { + "file": "_arraysetops_impl.py", + "line": 348, + "function": "_unique1d", + "primitive_calls": 187, + "calls": 196, + "self_s": 0.001800886, + "cumulative_s": 0.026925182000000002 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 2.7358e-05, + "cumulative_s": 0.026828008 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000319341, + "cumulative_s": 0.0389525 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.000185624, + "cumulative_s": 0.00028280300000000004 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.347555104999998, + "warm_predict_median_s": 0.685733349000003 + }, + { + "strategy": "legacy_cuda", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 2.3836052589999923, + "predict_s": 0.16347517999999184, + "metrics": { + "nll": 1.1134345890127653, + "crps": 0.41006694811656236, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "4708cd18d0529655506717eed0ed525c70beea728096b5e55bb49496fcb2d54e", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 0.14764312500000187, + "predict_s": 0.07034978999999453, + "metrics": { + "nll": 1.1134345833039598, + "crps": 0.410066942768244, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "fd24d23c33e311c0465a0877647d351459926d965e7f65b0cddfa7b794b4a66f", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 0.14715048899999772, + "predict_s": 0.07007901200000788, + "metrics": { + "nll": 1.1134345837862327, + "crps": 0.4100669409712408, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "b292ba175c140579e9604664e8219772d919f6da35d49e4c0e76da184ba376bf", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 0.14784342400000128, + "predict_s": 0.06987203799999975, + "metrics": { + "nll": 1.1134345841192845, + "crps": 0.4100669441894408, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "b3c432473e9c5148206a3bbe60b2018abc88997e5b3f847f6a0bd9e5d60091e9", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.2532061459999966, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.02031733600000507 + }, + "legacy_native_tree": { + "calls": 60, + "inclusive_s": 0.18122104299999364 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 31, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.5949000000000003e-05, + "cumulative_s": 0.25308260600000004 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.698e-05, + "cumulative_s": 0.25300198300000004 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.0008466260000000001, + "cumulative_s": 0.25279693200000003 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 90, + "self_s": 0.000632556, + "cumulative_s": 0.204296908 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000500642, + "cumulative_s": 0.178790992 + }, + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0008752810000000001, + "cumulative_s": 0.17049001800000002 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0030475190000000003, + "cumulative_s": 0.16961473700000002 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.003160299, + "cumulative_s": 0.16579422800000002 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.023985571, + "cumulative_s": 0.153134293 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.027724916000000002, + "cumulative_s": 0.100987039 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.0016410770000000001, + "cumulative_s": 0.083947727 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.58e-05, + "cumulative_s": 0.07552919000000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00046106300000000005, + "cumulative_s": 0.075477162 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.7138e-05, + "cumulative_s": 0.053783806 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.3867e-05, + "cumulative_s": 0.053661603 + }, + { + "file": "api.py", + "line": 116, + "function": "to_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000127414, + "cumulative_s": 0.048633321 + }, + { + "file": "devicearray.py", + "line": 910, + "function": "auto_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 7.632e-05, + "cumulative_s": 0.048347667000000004 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.0762000000000002e-05, + "cumulative_s": 0.047618984 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 1.8583e-05, + "cumulative_s": 0.047417557000000006 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00045709500000000003, + "cumulative_s": 0.047374952000000005 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.020764522, + "cumulative_s": 0.046786757000000005 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.6505e-05, + "cumulative_s": 0.045328032000000004 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.6963e-05, + "cumulative_s": 0.04493987 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000207542, + "cumulative_s": 0.044842149000000005 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4760, + "calls": 4760, + "self_s": 0.012702446000000001, + "cumulative_s": 0.044474458 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 6.502000000000001e-06, + "cumulative_s": 0.041483851 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.238e-06, + "cumulative_s": 0.041473615000000005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000253626, + "cumulative_s": 0.041457325 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2331, + "calls": 2331, + "self_s": 0.002225574, + "cumulative_s": 0.033103621 + }, + { + "file": "devices.py", + "line": 147, + "function": "_get_or_create_context_uncached", + "primitive_calls": 2015, + "calls": 2015, + "self_s": 0.008600268000000001, + "cumulative_s": 0.030104532000000003 + }, + { + "file": "devicearray.py", + "line": 66, + "function": "__init__", + "primitive_calls": 316, + "calls": 316, + "self_s": 0.001309391, + "cumulative_s": 0.029271792 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 1715, + "calls": 1715, + "self_s": 0.0008398, + "cumulative_s": 0.029189894 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0015602830000000002, + "cumulative_s": 0.027186569 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 2.3352000000000003e-05, + "cumulative_s": 0.027020842000000003 + }, + { + "file": "devicearray.py", + "line": 205, + "function": "copy_to_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.4545000000000003e-05, + "cumulative_s": 0.025632695 + }, + { + "file": "driver.py", + "line": 2645, + "function": "host_to_device", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.8333000000000002e-05, + "cumulative_s": 0.025254415000000002 + }, + { + "file": "threading.py", + "line": 1016, + "function": "_bootstrap", + "primitive_calls": 1, + "calls": 21, + "self_s": 0.00012004200000000001, + "cumulative_s": 0.024395644 + }, + { + "file": "devicearray.py", + "line": 850, + "function": "from_array_like", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.6255e-05, + "cumulative_s": 0.024047193 + }, + { + "file": "_objectives.py", + "line": 117, + "function": "init_raw", + "primitive_calls": 1, + "calls": 1, + "self_s": 5.198e-06, + "cumulative_s": 0.023429077000000003 + }, + { + "file": "_distributions.py", + "line": 384, + "function": "init_params", + "primitive_calls": 1, + "calls": 1, + "self_s": 1.1361e-05, + "cumulative_s": 0.023423253 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000500642, + "cumulative_s": 0.178790992 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000130276, + "cumulative_s": 0.019132124 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.0016361140000000001, + "cumulative_s": 0.009137639000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 0.14764312500000187, + "warm_predict_median_s": 0.07007901200000788 + }, + { + "strategy": "experimental_cuda", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 19.41882994300002, + "predict_s": 0.7416124009999976, + "metrics": { + "nll": 1.1134345229760074, + "crps": 0.41006692299465436, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "e0593f99b8f42b4d71e1ec1e9657aaa65d2bd258694ad0ad74ec3319ffbbebba", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 1.8863930159999995, + "predict_s": 0.7461580499999911, + "metrics": { + "nll": 1.1134345405060246, + "crps": 0.4100669279325746, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "26e37850e6f27603929c14ecd2be99857a9baf8fc4308c4e36c9d66c129f2523", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 1.8536638180000011, + "predict_s": 0.77138518000001, + "metrics": { + "nll": 1.1134345535080632, + "crps": 0.4100669362157608, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "aa4f0785623130a65809565fe7371aa51cf24168ff2f639f8e847f1c75686289", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 1.8499161659999857, + "predict_s": 0.7536495100000025, + "metrics": { + "nll": 1.1134345219266923, + "crps": 0.41006692163083897, + "coverage90": 0.9639050387596899 + }, + "fallback_warnings": [], + "prediction_sha256": "8ba9698478c351be32bf008c8039967a28c48931c1e6e6b0068a68144b496e12", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.014793961999999, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.030559718999938923 + }, + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.08635172900005728 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.3466991050000274 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 1.8822545989998218 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 2097152, + "samples": 367, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.00018150300000000001, + "cumulative_s": 2.014623906 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.2466000000000004e-05, + "cumulative_s": 2.0141039160000003 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.005136505, + "cumulative_s": 2.014015746 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 180, + "self_s": 0.003435271, + "cumulative_s": 1.9956077600000002 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.39235487900000005, + "cumulative_s": 1.8776857560000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.056670297, + "cumulative_s": 1.341745382 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.599337336, + "cumulative_s": 0.668082127 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.098279134, + "cumulative_s": 0.20119573100000002 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.096475999, + "cumulative_s": 0.191588161 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.025283465, + "cumulative_s": 0.146831113 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.0037206480000000004, + "cumulative_s": 0.14038187800000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.13379216200000002, + "cumulative_s": 0.13379216200000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.12293192800000001, + "cumulative_s": 0.12293192800000001 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006701, + "cumulative_s": 0.106720673 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002743483, + "cumulative_s": 0.102255309 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018764749, + "cumulative_s": 0.080509387 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.6848e-05, + "cumulative_s": 0.07147708800000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00044854100000000005, + "cumulative_s": 0.07140874200000001 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.014639701000000001, + "cumulative_s": 0.065279194 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.001487767, + "cumulative_s": 0.064603507 + }, + { + "file": "_device.py", + "line": 56, + "function": "", + "primitive_calls": 120, + "calls": 120, + "self_s": 0.0030701160000000003, + "cumulative_s": 0.061175267000000005 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029826131000000002, + "cumulative_s": 0.060656190000000006 + }, + { + "file": "search.py", + "line": 177, + "function": "where", + "primitive_calls": 1320, + "calls": 1320, + "self_s": 0.026979852000000002, + "cumulative_s": 0.054726910000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.8169e-05, + "cumulative_s": 0.051550118000000006 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.2413e-05, + "cumulative_s": 0.051379350000000004 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.4828e-05, + "cumulative_s": 0.045054497000000006 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.5151e-05, + "cumulative_s": 0.044852852000000006 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000278381, + "cumulative_s": 0.044800867 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.1824000000000005e-05, + "cumulative_s": 0.043361394000000004 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.2499000000000003e-05, + "cumulative_s": 0.043030094000000005 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00016296100000000001, + "cumulative_s": 0.042939781 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.249e-06, + "cumulative_s": 0.037731498 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.0097e-05, + "cumulative_s": 0.037719346 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000306919, + "cumulative_s": 0.03769957 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009462442, + "cumulative_s": 0.037263956 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.020356148, + "cumulative_s": 0.036858519 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00031646100000000003, + "cumulative_s": 0.035429328 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1500, + "calls": 1500, + "self_s": 0.031589525, + "cumulative_s": 0.031632457 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00017461400000000002, + "cumulative_s": 0.029020800000000003 + }, + { + "file": "_batch_split.py", + "line": 165, + "function": "", + "primitive_calls": 900, + "calls": 900, + "self_s": 0.000815348, + "cumulative_s": 0.028825538 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.39235487900000005, + "cumulative_s": 1.8776857560000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.056670297, + "cumulative_s": 1.341745382 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006701, + "cumulative_s": 0.106720673 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00017461400000000002, + "cumulative_s": 0.029020800000000003 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000264234, + "cumulative_s": 0.006667475000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 1.8536638180000011, + "warm_predict_median_s": 0.7536495100000025 + }, + { + "strategy": "extensions_cuda", + "seed": 1, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 20.403853507999997, + "predict_s": 0.7507841400000075, + "metrics": { + "nll": 1.5112631593488075, + "crps": 0.6181142100848838, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "61b18263523028fa82564f0377ee7d2bbc8377c0fb18479e92ed4717b8016ab9", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 1.8673297089999892, + "predict_s": 0.7591563850000114, + "metrics": { + "nll": 1.5112631514850416, + "crps": 0.6181142053327671, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "383628ea6384beaaf0a40926fcf587c5ef521769dbf24ae9e314e1b86c04afe4", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 1.9187017449999928, + "predict_s": 0.7644515360000241, + "metrics": { + "nll": 1.511263165020545, + "crps": 0.6181142165028344, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "0b32c5b79942bb2843c3e30dc31d89654dc1dd0aacc146e627ec5e5736d4e01d", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 1.9211320080000007, + "predict_s": 0.7504251130000057, + "metrics": { + "nll": 1.511263165407539, + "crps": 0.6181142165824589, + "coverage90": 0.9156976744186046 + }, + "fallback_warnings": [], + "prediction_sha256": "0b32c5b79942bb2843c3e30dc31d89654dc1dd0aacc146e627ec5e5736d4e01d", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.0509372559999974, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.10995661200004747 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.35238460700009 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 1.8971915599998397 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 369, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 2044928 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.00015964200000000001, + "cumulative_s": 2.0507749420000003 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 2.5524e-05, + "cumulative_s": 2.050311322 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.004960793000000001, + "cumulative_s": 2.0502843840000002 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 150, + "self_s": 0.0030618430000000003, + "cumulative_s": 2.011164161 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.40039457300000003, + "cumulative_s": 1.8927955250000001 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.05486767, + "cumulative_s": 1.347630032 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.600921665, + "cumulative_s": 0.6699759190000001 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.098569445, + "cumulative_s": 0.202650765 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.097196211, + "cumulative_s": 0.19257665200000001 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.025641801000000002, + "cumulative_s": 0.15059034400000001 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.003705163, + "cumulative_s": 0.142194755 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.138372883, + "cumulative_s": 0.138372883 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.137483916, + "cumulative_s": 0.137483916 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006602667, + "cumulative_s": 0.10802856100000001 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.0027035790000000002, + "cumulative_s": 0.10211586700000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 3.7302e-05, + "cumulative_s": 0.074834627 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00043130100000000004, + "cumulative_s": 0.074777382 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004724694000000001, + "cumulative_s": 0.06276178 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.030248324000000004, + "cumulative_s": 0.06120447800000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.001650229, + "cumulative_s": 0.057172796000000005 + }, + { + "file": "search.py", + "line": 177, + "function": "where", + "primitive_calls": 1320, + "calls": 1320, + "self_s": 0.02763124, + "cumulative_s": 0.055661115000000004 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.1032e-05, + "cumulative_s": 0.053688761 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.733200000000001e-05, + "cumulative_s": 0.053534881000000006 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.0285000000000002e-05, + "cumulative_s": 0.047172822 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.9868000000000003e-05, + "cumulative_s": 0.046970118000000005 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000247505, + "cumulative_s": 0.046893093000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 309, + "calls": 309, + "self_s": 0.0021115920000000002, + "cumulative_s": 0.045709226000000006 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 4.1929000000000005e-05, + "cumulative_s": 0.045521231 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.6711e-05, + "cumulative_s": 0.045181719 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00027083500000000003, + "cumulative_s": 0.045085995000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 2.0909000000000003e-05, + "cumulative_s": 0.043988905 + }, + { + "file": "_methods.py", + "line": 66, + "function": "_all", + "primitive_calls": 2, + "calls": 2, + "self_s": 1.1203000000000001e-05, + "cumulative_s": 0.043806299 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.010261022, + "cumulative_s": 0.04227928 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.008422844, + "cumulative_s": 0.040126164000000006 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000328279, + "cumulative_s": 0.039850309 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.014191251, + "cumulative_s": 0.039196331 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 2.1035e-05, + "cumulative_s": 0.038661647 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.407000000000001e-06, + "cumulative_s": 0.038636742 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00034481100000000004, + "cumulative_s": 0.038620575000000004 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009219403000000001, + "cumulative_s": 0.037421005 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.40039457300000003, + "cumulative_s": 1.8927955250000001 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.05486767, + "cumulative_s": 1.347630032 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006602667, + "cumulative_s": 0.10802856100000001 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00026847000000000004, + "cumulative_s": 0.006695282 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 1.9187017449999928, + "warm_predict_median_s": 0.7591563850000114 + }, + { + "strategy": "legacy_cpu", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 4.379804434999983, + "predict_s": 0.6876178470000127, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + }, + { + "phase": "warm_1", + "fit_s": 2.365520406999991, + "predict_s": 0.6849535100000139, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + }, + { + "phase": "warm_2", + "fit_s": 2.3647265090000076, + "predict_s": 0.6862471620000008, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + }, + { + "phase": "warm_3", + "fit_s": 2.3730133250000165, + "predict_s": 0.6879947869999796, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "actual_device": "cpu" + } + ], + "profile": { + "wall_s": 2.351503859000019, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.013973575999983723 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "not applicable to CPU" + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 5.4843e-05, + "cumulative_s": 2.351418662 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.1644000000000004e-05, + "cumulative_s": 2.351337446 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.002442533, + "cumulative_s": 2.351305136 + }, + { + "file": "_growth.py", + "line": 280, + "function": "__call__", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00027971800000000003, + "cumulative_s": 1.9989263160000001 + }, + { + "file": "_growth.py", + "line": 267, + "function": "predict", + "primitive_calls": 60, + "calls": 60, + "self_s": 8.487100000000001e-05, + "cumulative_s": 1.9985466610000002 + }, + { + "file": "_growth.py", + "line": 294, + "function": "_predict_standard", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00012133900000000001, + "cumulative_s": 1.9984617900000001 + }, + { + "file": "_growth.py", + "line": 300, + "function": "_predict_standard_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 1.9957801810000002, + "cumulative_s": 1.998218344 + }, + { + "file": "_tree.py", + "line": 306, + "function": "fit_tree", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0011128940000000001, + "cumulative_s": 0.31298151700000004 + }, + { + "file": "_growth.py", + "line": 493, + "function": "grow", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.004799836, + "cumulative_s": 0.31057442100000004 + }, + { + "file": "_primitives.py", + "line": 71, + "function": "build_node_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0007126280000000001, + "cumulative_s": 0.09497841100000001 + }, + { + "file": "_primitives.py", + "line": 106, + "function": "_build_node_histograms_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.057323259, + "cumulative_s": 0.09389412100000001 + }, + { + "file": "_primitives.py", + "line": 511, + "function": "partition_samples", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00043712700000000005, + "cumulative_s": 0.080948536 + }, + { + "file": "_primitives.py", + "line": 544, + "function": "_partition_samples_cpu", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.07232838100000001, + "cumulative_s": 0.08001738800000001 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.9377000000000005e-05, + "cumulative_s": 0.070843852 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00044155800000000004, + "cumulative_s": 0.070649719 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.1164e-05, + "cumulative_s": 0.051612902 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.2439e-05, + "cumulative_s": 0.051468860000000005 + }, + { + "file": "_primitives.py", + "line": 401, + "function": "find_node_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.004210082, + "cumulative_s": 0.045104409000000005 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.4253000000000003e-05, + "cumulative_s": 0.044705065 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.1326000000000002e-05, + "cumulative_s": 0.044526174 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00027827600000000004, + "cumulative_s": 0.04448299 + }, + { + "file": "_primitives.py", + "line": 735, + "function": "compute_leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.00012246500000000001, + "cumulative_s": 0.044236181 + }, + { + "file": "_primitives.py", + "line": 766, + "function": "_compute_leaf_values_cpu", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.035943049000000005, + "cumulative_s": 0.043988671 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.7858000000000003e-05, + "cumulative_s": 0.042854787000000005 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 1.9693000000000003e-05, + "cumulative_s": 0.042564942 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000144881, + "cumulative_s": 0.042478465 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 9.259e-06, + "cumulative_s": 0.037946508000000004 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 9.932e-06, + "cumulative_s": 0.037933859 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000319806, + "cumulative_s": 0.037916301 + }, + { + "file": "_split.py", + "line": 46, + "function": "find_best_split", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.003276376, + "cumulative_s": 0.037444614 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000289188, + "cumulative_s": 0.03631313 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000341052, + "cumulative_s": 0.035961118 + }, + { + "file": "threading.py", + "line": 1115, + "function": "join", + "primitive_calls": 3, + "calls": 3, + "self_s": 0.000100655, + "cumulative_s": 0.035168005 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 130, + "calls": 190, + "self_s": 0.00047968900000000005, + "cumulative_s": 0.035054025 + }, + { + "file": "threading.py", + "line": 1153, + "function": "_wait_for_tstate_lock", + "primitive_calls": 40, + "calls": 40, + "self_s": 3.9147e-05, + "cumulative_s": 0.034967602 + }, + { + "file": "_distributions.py", + "line": 211, + "function": "natural_gradient", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.004266223, + "cumulative_s": 0.034112057 + }, + { + "file": "_cpu.py", + "line": 118, + "function": "find_best_split_cpu", + "primitive_calls": 420, + "calls": 420, + "self_s": 0.025266586, + "cumulative_s": 0.031559960000000005 + }, + { + "file": "_arraysetops_impl.py", + "line": 145, + "function": "unique", + "primitive_calls": 188, + "calls": 196, + "self_s": 0.000686307, + "cumulative_s": 0.028255276000000003 + }, + { + "file": "_cpu.py", + "line": 42, + "function": "build_histogram_cpu", + "primitive_calls": 240, + "calls": 240, + "self_s": 0.026694823000000003, + "cumulative_s": 0.027726366000000002 + }, + { + "file": "_growth.py", + "line": 702, + "function": "_get_active_nodes", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.0016723740000000001, + "cumulative_s": 0.027132711 + } + ], + "path_functions": [ + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000341052, + "cumulative_s": 0.035961118 + } + ], + "named_transfer_functions": [ + { + "file": "_trainer.py", + "line": 51, + "function": "_to_host", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.00019523700000000001, + "cumulative_s": 0.00032748 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 2.365520406999991, + "warm_predict_median_s": 0.6862471620000008 + }, + { + "strategy": "legacy_cuda", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 2.3314647660000105, + "predict_s": 0.15972930300000598, + "metrics": { + "nll": 1.096392018592626, + "crps": 0.40381985608350923, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "b7528d8336d2099ad5ac9bb904815935853041f9ecccf412c6c7b120da6905e5", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 0.16209129700001768, + "predict_s": 0.07248722799999996, + "metrics": { + "nll": 1.0963920473159217, + "crps": 0.40381986871519576, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "bc35c366a39ac16d3bb46f23eac52050f22043e2c6b7b3ddec06de010f9cabaf", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 0.14284484199998815, + "predict_s": 0.06907718700000487, + "metrics": { + "nll": 1.0963920313096218, + "crps": 0.4038198599814491, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "6c7cf04f5b795efec4c16211429246cde946f0bbf9d7eb6166cdfd49b1e93995", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 0.14493832599998768, + "predict_s": 0.07202352100000553, + "metrics": { + "nll": 1.0963920433337724, + "crps": 0.40381986731829195, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "17cb47c468a9769a9b4b5c3ba6e7b4084449dfbd65c52df68edebb567d491d85", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 0.25264425700001425, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.02037358399996947 + }, + "legacy_native_tree": { + "calls": 60, + "inclusive_s": 0.181848511000112 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 330235904, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 330235904, + "sampled_peak_delta_bytes": 0, + "samples": 30, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 0 + }, + "top_host_functions": [ + { + "file": "_distributional.py", + "line": 185, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.4853000000000006e-05, + "cumulative_s": 0.252541414 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.0842e-05, + "cumulative_s": 0.25247079 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.00082554, + "cumulative_s": 0.25224772300000003 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 90, + "self_s": 0.000646457, + "cumulative_s": 0.204921084 + }, + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000498135, + "cumulative_s": 0.179339939 + }, + { + "file": "dispatcher.py", + "line": 718, + "function": "__call__", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.00093378, + "cumulative_s": 0.171109055 + }, + { + "file": "dispatcher.py", + "line": 1635, + "function": "call", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0030685260000000002, + "cumulative_s": 0.17017527500000001 + }, + { + "file": "_cuda.py", + "line": 2863, + "function": "build_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.0031492380000000004, + "cumulative_s": 0.16638502900000002 + }, + { + "file": "dispatcher.py", + "line": 482, + "function": "launch", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.025002782, + "cumulative_s": 0.15367684 + }, + { + "file": "dispatcher.py", + "line": 551, + "function": "_prepare_args", + "primitive_calls": 8760, + "calls": 8760, + "self_s": 0.027698234000000002, + "cumulative_s": 0.100691478 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.0400000000000005e-05, + "cumulative_s": 0.073550497 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000504319, + "cumulative_s": 0.073467378 + }, + { + "file": "devices.py", + "line": 230, + "function": "_require_cuda_context", + "primitive_calls": 604, + "calls": 616, + "self_s": 0.001568921, + "cumulative_s": 0.057901986 + }, + { + "file": "args.py", + "line": 48, + "function": "to_device", + "primitive_calls": 5310, + "calls": 5310, + "self_s": 0.020535836000000002, + "cumulative_s": 0.04691068 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.3044000000000004e-05, + "cumulative_s": 0.044378119 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.2015000000000002e-05, + "cumulative_s": 0.04409148 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00017488600000000002, + "cumulative_s": 0.044008244 + }, + { + "file": "driver.py", + "line": 348, + "function": "safe_cuda_api_call", + "primitive_calls": 4760, + "calls": 4760, + "self_s": 0.013041387000000002, + "cumulative_s": 0.041134790000000004 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 5.511e-06, + "cumulative_s": 0.037418071000000004 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.936e-06, + "cumulative_s": 0.037409155 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00028312300000000003, + "cumulative_s": 0.037394084 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 7.363500000000001e-05, + "cumulative_s": 0.036872358 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 9.680800000000001e-05, + "cumulative_s": 0.036688987 + }, + { + "file": "devices.py", + "line": 131, + "function": "get_or_create_context", + "primitive_calls": 2331, + "calls": 2331, + "self_s": 0.002255767, + "cumulative_s": 0.033046571000000004 + }, + { + "file": "devices.py", + "line": 147, + "function": "_get_or_create_context_uncached", + "primitive_calls": 2015, + "calls": 2015, + "self_s": 0.008519479, + "cumulative_s": 0.030064574 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.684e-05, + "cumulative_s": 0.029380309 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.5517000000000003e-05, + "cumulative_s": 0.029202323000000002 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.000189719, + "cumulative_s": 0.029138296 + }, + { + "file": "devices.py", + "line": 216, + "function": "get_context", + "primitive_calls": 1715, + "calls": 1715, + "self_s": 0.000840309, + "cumulative_s": 0.029100632 + }, + { + "file": "codegen.py", + "line": 338, + "function": "get_cufunc", + "primitive_calls": 1230, + "calls": 1230, + "self_s": 0.0014882670000000001, + "cumulative_s": 0.027039418000000003 + }, + { + "file": "pool.py", + "line": 500, + "function": "_wait_for_updates", + "primitive_calls": 2, + "calls": 2, + "self_s": 3.4887e-05, + "cumulative_s": 0.025935417000000002 + }, + { + "file": "devicearray.py", + "line": 66, + "function": "__init__", + "primitive_calls": 316, + "calls": 316, + "self_s": 0.001310437, + "cumulative_s": 0.025612315 + }, + { + "file": "driver.py", + "line": 1206, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.00010442100000000001, + "cumulative_s": 0.023971002 + }, + { + "file": "driver.py", + "line": 929, + "function": "memalloc", + "primitive_calls": 124, + "calls": 124, + "self_s": 0.000625648, + "cumulative_s": 0.023863176 + }, + { + "file": "_objectives.py", + "line": 117, + "function": "init_raw", + "primitive_calls": 1, + "calls": 1, + "self_s": 8.022e-06, + "cumulative_s": 0.023673524 + }, + { + "file": "_distributions.py", + "line": 384, + "function": "init_params", + "primitive_calls": 1, + "calls": 1, + "self_s": 1.8732e-05, + "cumulative_s": 0.023664196000000002 + }, + { + "file": "fromnumeric.py", + "line": 3869, + "function": "std", + "primitive_calls": 1, + "calls": 1, + "self_s": 3.9005000000000003e-05, + "cumulative_s": 0.023643935 + }, + { + "file": "_methods.py", + "line": 220, + "function": "_std", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.3871e-05, + "cumulative_s": 0.023373090000000003 + }, + { + "file": "_methods.py", + "line": 150, + "function": "_var", + "primitive_calls": 1, + "calls": 1, + "self_s": 9.667700000000001e-05, + "cumulative_s": 0.022590913 + }, + { + "file": "threading.py", + "line": 1016, + "function": "_bootstrap", + "primitive_calls": 1, + "calls": 21, + "self_s": 0.000147388, + "cumulative_s": 0.022468706 + } + ], + "path_functions": [ + { + "file": "_tree.py", + "line": 220, + "function": "fit_tree_gpu_native", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000498135, + "cumulative_s": 0.179339939 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00013092, + "cumulative_s": 0.019166311000000002 + } + ], + "named_transfer_functions": [ + { + "file": "devicearray.py", + "line": 240, + "function": "copy_to_host", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.0016367360000000002, + "cumulative_s": 0.009365221 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 0.14493832599998768, + "warm_predict_median_s": 0.07202352100000553 + }, + { + "strategy": "experimental_cuda", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 19.369313422000005, + "predict_s": 0.749904894000025, + "metrics": { + "nll": 1.0963920302696062, + "crps": 0.40381987231099215, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "f682237260cb02a49d83de9d3759b83c16021d3f70c156ce821f4fdf4680f1bf", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 1.8857443009999884, + "predict_s": 0.7335166679999929, + "metrics": { + "nll": 1.0963920201794455, + "crps": 0.4038198636749877, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "f97675d2a21ebd85e5ce2fe5fa648073b37d78126fac77a38f6300c02cdccd91", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 1.8722109340000088, + "predict_s": 0.7539939800000184, + "metrics": { + "nll": 1.0963919966373372, + "crps": 0.40381985429694445, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "250264765b6c77df7db2b9791501413258246829d48ef466ce8961b02227f7df", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 1.8661910969999838, + "predict_s": 0.7456259799999998, + "metrics": { + "nll": 1.0963920038570387, + "crps": 0.4038198545856173, + "coverage90": 0.9643895348837209 + }, + "fallback_warnings": [], + "prediction_sha256": "c35eb442f6d427a867d3d10cb7548ca554ca1ec8b125512bfd535a1d21ad4abf", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.0116760339999757, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "legacy_objective": { + "calls": 30, + "inclusive_s": 0.029579955000031077 + }, + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.0838815360001206 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.3430705719999594 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 1.8804903539998463 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 334430208, + "sampled_peak_delta_bytes": 2097152, + "samples": 365, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 1252864 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.00018485300000000001, + "cumulative_s": 2.011514462 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 4.7834e-05, + "cumulative_s": 2.0109851350000003 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.005081427, + "cumulative_s": 2.0108667980000003 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 180, + "self_s": 0.0033895030000000003, + "cumulative_s": 1.9685735460000002 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.394663588, + "cumulative_s": 1.8760057490000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.055348124000000005, + "cumulative_s": 1.338298605 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.599717257, + "cumulative_s": 0.668149054 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.09854842200000001, + "cumulative_s": 0.201272937 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.095846131, + "cumulative_s": 0.19061213600000002 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.025280821000000002, + "cumulative_s": 0.146238813 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.0036855890000000004, + "cumulative_s": 0.13971276300000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2400, + "calls": 2400, + "self_s": 0.133318389, + "cumulative_s": 0.133318389 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2190, + "calls": 2190, + "self_s": 0.121729789, + "cumulative_s": 0.121729789 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.002671928, + "cumulative_s": 0.10153273600000001 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006642688000000001, + "cumulative_s": 0.082012132 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.7721000000000006e-05, + "cumulative_s": 0.07315421700000001 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.000491966, + "cumulative_s": 0.07308644 + }, + { + "file": "from_data.py", + "line": 56, + "function": "asarray", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.000290991, + "cumulative_s": 0.064083771 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 422, + "calls": 422, + "self_s": 0.017339497000000002, + "cumulative_s": 0.06370547600000001 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029921037, + "cumulative_s": 0.060577563 + }, + { + "file": "basic.py", + "line": 298, + "function": "full", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.009230017, + "cumulative_s": 0.059496227000000006 + }, + { + "file": "search.py", + "line": 177, + "function": "where", + "primitive_calls": 1320, + "calls": 1320, + "self_s": 0.027445481, + "cumulative_s": 0.055247546 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 5.9316000000000006e-05, + "cumulative_s": 0.052913873 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.114500000000001e-05, + "cumulative_s": 0.052777097 + }, + { + "file": "basic.py", + "line": 12, + "function": "copyto", + "primitive_calls": 962, + "calls": 962, + "self_s": 0.02593389, + "cumulative_s": 0.050266210000000006 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.3804000000000004e-05, + "cumulative_s": 0.045746809 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.4437e-05, + "cumulative_s": 0.045563919 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00022976200000000003, + "cumulative_s": 0.045513429 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.3662000000000004e-05, + "cumulative_s": 0.04435795 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.5093000000000002e-05, + "cumulative_s": 0.04404533 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.00014711000000000002, + "cumulative_s": 0.043958954 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.014679813000000002, + "cumulative_s": 0.039511304000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 573, + "calls": 573, + "self_s": 0.0013896910000000002, + "cumulative_s": 0.038652805000000005 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018699196, + "cumulative_s": 0.03758255 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 4.118e-06, + "cumulative_s": 0.036954039 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 6.683e-06, + "cumulative_s": 0.036947483 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.00029755900000000003, + "cumulative_s": 0.036934305 + }, + { + "file": "_batch_leaf.py", + "line": 100, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.020121109, + "cumulative_s": 0.036605998 + }, + { + "file": "_contracts.py", + "line": 75, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000283637, + "cumulative_s": 0.034343156 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 1500, + "calls": 1500, + "self_s": 0.031699248, + "cumulative_s": 0.031742158 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.394663588, + "cumulative_s": 1.8760057490000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.055348124000000005, + "cumulative_s": 1.338298605 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006642688000000001, + "cumulative_s": 0.082012132 + }, + { + "file": "_objectives.py", + "line": 139, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.000160864, + "cumulative_s": 0.028119626 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.000257887, + "cumulative_s": 0.006658628000000001 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 1.8722109340000088, + "warm_predict_median_s": 0.7456259799999998 + }, + { + "strategy": "extensions_cuda", + "seed": 2, + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "mode": "resident", + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "process_first", + "fit_s": 20.599866340999995, + "predict_s": 0.7390843580000137, + "metrics": { + "nll": 1.494126317810867, + "crps": 0.6068144263657685, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "4fdc57a17b850f2bc3ebc76f142e4db66685b87161989d18985c16ffc1086615", + "actual_device": "cuda" + }, + { + "phase": "warm_1", + "fit_s": 1.8874415239999962, + "predict_s": 0.7386861040000099, + "metrics": { + "nll": 1.4941263226742383, + "crps": 0.6068144272363147, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "fbacc07489c4688599856f30dcff7eebc729727ab1158dbcb9a073f144ee28a5", + "actual_device": "cuda" + }, + { + "phase": "warm_2", + "fit_s": 2.156747376999988, + "predict_s": 0.7374961950000056, + "metrics": { + "nll": 1.4941263226742383, + "crps": 0.6068144272363147, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "fbacc07489c4688599856f30dcff7eebc729727ab1158dbcb9a073f144ee28a5", + "actual_device": "cuda" + }, + { + "phase": "warm_3", + "fit_s": 1.8894675399999983, + "predict_s": 0.7384743080000362, + "metrics": { + "nll": 1.4941263226742383, + "crps": 0.6068144272363147, + "coverage90": 0.9224806201550387 + }, + "fallback_warnings": [], + "prediction_sha256": "fbacc07489c4688599856f30dcff7eebc729727ab1158dbcb9a073f144ee28a5", + "actual_device": "cuda" + } + ], + "profile": { + "wall_s": 2.0386829059999627, + "isolated_host_profile": true, + "synchronized_inclusive_timers": { + "extension_objective_boundary": { + "calls": 30, + "inclusive_s": 0.10954144599992333 + }, + "levelwise_builder": { + "calls": 60, + "inclusive_s": 1.347369284999786 + }, + "extension_tree_boundary": { + "calls": 60, + "inclusive_s": 1.8852034829999411 + } + }, + "timer_scope": "separate profile fit with synchronized nested boundaries; overlaps and synchronization overhead, not production timing", + "memory": { + "scope": "separate memory-only warm fit, no cProfile; 5 ms device-wide samples including contexts/caches; lower bound, not exact per-fit peak", + "initial_used_bytes": 332333056, + "total_bytes": 15637086208, + "sampled_peak_used_bytes": 332333056, + "sampled_peak_delta_bytes": 0, + "samples": 369, + "errors": [], + "cupy_pool_used_bytes": 0, + "cupy_pool_total_bytes": 2044928 + }, + "top_host_functions": [ + { + "file": "_booster.py", + "line": 80, + "function": "fit", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.000152761, + "cumulative_s": 2.038542253 + }, + { + "file": "_trainer.py", + "line": 137, + "function": "wrapped", + "primitive_calls": 1, + "calls": 1, + "self_s": 2.3248000000000002e-05, + "cumulative_s": 2.0380772620000003 + }, + { + "file": "_trainer.py", + "line": 151, + "function": "fit_boosting", + "primitive_calls": 1, + "calls": 1, + "self_s": 0.004906843, + "cumulative_s": 2.038052316 + }, + { + "file": "value_worker.py", + "line": 146, + "function": "wrapper", + "primitive_calls": 90, + "calls": 150, + "self_s": 0.003061203, + "cumulative_s": 1.998894404 + }, + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.396205332, + "cumulative_s": 1.8808385600000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.055367330000000006, + "cumulative_s": 1.3426090430000002 + }, + { + "file": "_batch_split.py", + "line": 187, + "function": "partition", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.599960166, + "cumulative_s": 0.66911679 + }, + { + "file": "_batch_split.py", + "line": 131, + "function": "find_splits", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.098487773, + "cumulative_s": 0.20140238400000002 + }, + { + "file": "_batch_primitives.py", + "line": 61, + "function": "build_histograms", + "primitive_calls": 180, + "calls": 180, + "self_s": 0.096756519, + "cumulative_s": 0.191372336 + }, + { + "file": "_batch_leaf.py", + "line": 121, + "function": "leaf_values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.025359577, + "cumulative_s": 0.149129177 + }, + { + "file": "truth.py", + "line": 55, + "function": "any", + "primitive_calls": 2430, + "calls": 2430, + "self_s": 0.003670365, + "cumulative_s": 0.14053977 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2490, + "calls": 2490, + "self_s": 0.13676669700000002, + "cumulative_s": 0.13676669700000002 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2460, + "calls": 2460, + "self_s": 0.135863344, + "cumulative_s": 0.135863344 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006563588, + "cumulative_s": 0.10769334200000001 + }, + { + "file": "truth.py", + "line": 18, + "function": "all", + "primitive_calls": 1740, + "calls": 1740, + "self_s": 0.0026798020000000002, + "cumulative_s": 0.101554585 + }, + { + "file": "connection.py", + "line": 390, + "function": "_recv", + "primitive_calls": 8, + "calls": 8, + "self_s": 4.2821e-05, + "cumulative_s": 0.076245958 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 8, + "calls": 8, + "self_s": 0.00048661200000000004, + "cumulative_s": 0.076182756 + }, + { + "file": "__init__.py", + "line": 68, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.00511333, + "cumulative_s": 0.062776756 + }, + { + "file": "_batch_leaf.py", + "line": 51, + "function": "reduce_leaves", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.029819092000000002, + "cumulative_s": 0.060554826000000006 + }, + { + "file": "connection.py", + "line": 1121, + "function": "wait", + "primitive_calls": 8, + "calls": 8, + "self_s": 6.5463e-05, + "cumulative_s": 0.055601889 + }, + { + "file": "selectors.py", + "line": 402, + "function": "select", + "primitive_calls": 8, + "calls": 8, + "self_s": 8.1215e-05, + "cumulative_s": 0.055455201 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 663, + "calls": 663, + "self_s": 0.0016335310000000001, + "cumulative_s": 0.055310108000000004 + }, + { + "file": "search.py", + "line": 177, + "function": "where", + "primitive_calls": 1320, + "calls": 1320, + "self_s": 0.027236688000000002, + "cumulative_s": 0.055303317000000005 + }, + { + "file": "queues.py", + "line": 369, + "function": "empty", + "primitive_calls": 6, + "calls": 6, + "self_s": 3.3396000000000003e-05, + "cumulative_s": 0.048188379 + }, + { + "file": "connection.py", + "line": 253, + "function": "poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 2.8594e-05, + "cumulative_s": 0.048017729 + }, + { + "file": "connection.py", + "line": 439, + "function": "_poll", + "primitive_calls": 6, + "calls": 6, + "self_s": 0.00030955800000000003, + "cumulative_s": 0.047942468 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 301, + "calls": 309, + "self_s": 0.002284894, + "cumulative_s": 0.046787142000000004 + }, + { + "file": "queues.py", + "line": 380, + "function": "get", + "primitive_calls": 4, + "calls": 4, + "self_s": 3.7522000000000004e-05, + "cumulative_s": 0.046118375 + }, + { + "file": "connection.py", + "line": 208, + "function": "recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 2.0342000000000003e-05, + "cumulative_s": 0.045822826000000004 + }, + { + "file": "connection.py", + "line": 429, + "function": "_recv_bytes", + "primitive_calls": 4, + "calls": 4, + "self_s": 0.000207862, + "cumulative_s": 0.045723375000000004 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 5, + "calls": 5, + "self_s": 2.4314e-05, + "cumulative_s": 0.045174377 + }, + { + "file": "_methods.py", + "line": 49, + "function": "_sum", + "primitive_calls": 5, + "calls": 5, + "self_s": 5.3822e-05, + "cumulative_s": 0.045049643 + }, + { + "file": "__init__.py", + "line": 37, + "function": "_terms", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.010002770000000001, + "cumulative_s": 0.041933182 + }, + { + "file": "__init__.py", + "line": 15, + "function": "_inputs", + "primitive_calls": 31, + "calls": 31, + "self_s": 0.008539, + "cumulative_s": 0.039790538 + }, + { + "file": "__init__.py", + "line": 18, + "function": "values", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.000329776, + "cumulative_s": 0.039531451 + }, + { + "file": "connection.py", + "line": 131, + "function": "__del__", + "primitive_calls": 2, + "calls": 2, + "self_s": 7.3020000000000006e-06, + "cumulative_s": 0.039437301 + }, + { + "file": "connection.py", + "line": 376, + "function": "_close", + "primitive_calls": 2, + "calls": 2, + "self_s": 8.963e-06, + "cumulative_s": 0.039426421 + }, + { + "file": "~", + "line": 0, + "function": "", + "primitive_calls": 2, + "calls": 2, + "self_s": 0.000300189, + "cumulative_s": 0.039407169000000006 + }, + { + "file": "comparison.py", + "line": 42, + "function": "array_equal", + "primitive_calls": 450, + "calls": 450, + "self_s": 0.013820444000000001, + "cumulative_s": 0.037995176000000006 + }, + { + "file": "_device.py", + "line": 12, + "function": "device_vector", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.018672798, + "cumulative_s": 0.037563579 + } + ], + "path_functions": [ + { + "file": "_device.py", + "line": 92, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.396205332, + "cumulative_s": 1.8808385600000002 + }, + { + "file": "_levelwise.py", + "line": 28, + "function": "build", + "primitive_calls": 60, + "calls": 60, + "self_s": 0.055367330000000006, + "cumulative_s": 1.3426090430000002 + }, + { + "file": "_device.py", + "line": 38, + "function": "step", + "primitive_calls": 30, + "calls": 30, + "self_s": 0.006563588, + "cumulative_s": 0.10769334200000001 + } + ], + "named_transfer_functions": [ + { + "file": "__init__.py", + "line": 782, + "function": "asnumpy", + "primitive_calls": 300, + "calls": 300, + "self_s": 0.00025507700000000004, + "cumulative_s": 0.0065508630000000005 + } + ], + "transfer_scope": "cProfile named wrappers only; nested calls overlap, scalar/internal copies excluded; not a total transfer audit", + "scope": "cProfile inclusive host times overlap; asynchronous launches are not kernel durations", + "nsys_available": false, + "cuda_trace_captured": false + }, + "worker_stderr": "", + "warm_fit_median_s": 1.8894675399999983, + "warm_predict_median_s": 0.7384743080000362 + } + ], + "frozen_baseline_cells": [ + { + "backend": "cpu", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.397831454999999, + "predict_params_s": 0.6745909410000017, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4014690210000005, + "predict_params_s": 0.6882345490000006, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 3.2421392509999976, + "predict_params_s": 0.19642429199999967, + "metrics": { + "nll": 1.0944737563673146, + "crps": 0.39859095157938995, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "f30ad0f19907a419c4106ddb23b3ed388782751464b971fb3ae4e231ef84368c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14386336900000174, + "predict_params_s": 0.0673137920000002, + "metrics": { + "nll": 1.0944737689342998, + "crps": 0.39859095595416205, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "a0feedfd12a1e785bd4b8fe1a82bf04da2a069ece3b8c37d812db27ce3af2963", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.106432963000003, + "predict_params_s": 0.6825809110000023, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6" + }, + { + "phase": "repeat_fit", + "fit_s": 3.026923318999998, + "predict_params_s": 0.6765377500000014, + "metrics": { + "nll": 1.0944737639506685, + "crps": 0.39859095006807804, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.485117793083191, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750824928283691, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711622714996338, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332436800003052, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "adfe69fcfd5eec3f8e575322aaa12382f95624c7697b28e91302fd5709c86bd6", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 0, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.450299663999999, + "predict_params_s": 0.01670187599999906, + "metrics": { + "nll": 1.0944737574085481, + "crps": 0.39859095178535353, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991605997085571, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "4ab822ab4020fe3163e0664f5c51b755060bffa203f544e45148aa66544d4aeb" + }, + { + "phase": "repeat_fit", + "fit_s": 0.22371609900000067, + "predict_params_s": 0.01638825799999921, + "metrics": { + "nll": 1.0944737612758777, + "crps": 0.3985909545795274, + "coverage90": 0.967781007751938 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5186489820480347, + 1.4851176738739014, + 1.4573966264724731, + 1.434107780456543, + 1.4131090641021729, + 1.3933724164962769, + 1.3750823736190796, + 1.3586803674697876, + 1.3432518243789673, + 1.3281663656234741, + 1.3132743835449219, + 1.2991604804992676, + 1.284942388534546, + 1.2711621522903442, + 1.2577455043792725, + 1.2455414533615112, + 1.2329634428024292, + 1.2209056615829468, + 1.2085977792739868, + 1.1973193883895874, + 1.186450481414795, + 1.1741472482681274, + 1.164169430732727, + 1.1534911394119263, + 1.1431702375411987, + 1.1332437992095947, + 1.121882438659668, + 1.1124082803726196, + 1.1032801866531372, + 1.0944879055023193 + ] + } + }, + "prediction_sha256": "794edadbc23c07f1590fc520e02046cda972173bd5c4fca1e3ca8566ca5dadb5", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.533779657000004, + "predict_params_s": 0.6762787880000047, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 2.4129718869999977, + "predict_params_s": 0.6843157980000001, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.407129374, + "predict_params_s": 0.16393877899999865, + "metrics": { + "nll": 1.113434598491401, + "crps": 0.4100669511518961, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "9111616954b6fb780447600cf7aec943b45d3e575b16cf188d1e03fc0e81ae1b" + }, + { + "phase": "repeat_fit", + "fit_s": 0.16078772999999558, + "predict_params_s": 0.06733073400000222, + "metrics": { + "nll": 1.1134345862813968, + "crps": 0.4100669440537582, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "26d571b4fae82b7b516354f105c2e48465aabc290c649e17b1450ff0274c8319", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.032100342, + "predict_params_s": 0.6659178060000102, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe" + }, + { + "phase": "repeat_fit", + "fit_s": 3.0121408979999984, + "predict_params_s": 0.6714607630000131, + "metrics": { + "nll": 1.1134345852429903, + "crps": 0.4100669436072279, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305916547775269, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "8791daee5c391356bc699790953da8b68b5e0fc32324f6905ce6a7ff4fb39ebe", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 1, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.386929182000003, + "predict_params_s": 0.01657929299999239, + "metrics": { + "nll": 1.1134345775848313, + "crps": 0.41006693913527403, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.420682668685913, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.2425525188446045, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872673034667969, + 1.1751006841659546, + 1.1648060083389282, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "f0446a567cc7ce8929da5b47732e69a48ba8fd76640e21a1c83b260e6094f652" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23034155599999906, + "predict_params_s": 0.016565403999990735, + "metrics": { + "nll": 1.1134345986050809, + "crps": 0.41006695480534144, + "coverage90": 0.9639050387596899 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5334731340408325, + 1.4967509508132935, + 1.4675382375717163, + 1.4424952268600464, + 1.4206825494766235, + 1.400489091873169, + 1.3827072381973267, + 1.3656353950500488, + 1.3492900133132935, + 1.3348757028579712, + 1.3207321166992188, + 1.306437373161316, + 1.2925642728805542, + 1.279238224029541, + 1.2673367261886597, + 1.2544152736663818, + 1.242552638053894, + 1.2305917739868164, + 1.2196043729782104, + 1.208702564239502, + 1.1979057788848877, + 1.1872674226760864, + 1.1751006841659546, + 1.1648061275482178, + 1.1546728610992432, + 1.144798755645752, + 1.13528311252594, + 1.126444935798645, + 1.117047667503357, + 1.1084883213043213 + ] + } + }, + "prediction_sha256": "45b3c964270056fd4530962345d490834970cdb3fffb7002c7f743235f090923", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 4.4131726219999905, + "predict_params_s": 0.6596325430000007, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 2.385363081999998, + "predict_params_s": 0.6809927400000078, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "resident", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.4124752700000016, + "predict_params_s": 0.1630236369999949, + "metrics": { + "nll": 1.0963920238037668, + "crps": 0.4038198588211825, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "29253dd5874532fcafc8c8c2bc392181213ae0ea9cbeef4d6297378ac1f3113c" + }, + { + "phase": "repeat_fit", + "fit_s": 0.14519270999998923, + "predict_params_s": 0.06746661900000106, + "metrics": { + "nll": 1.096392045323513, + "crps": 0.40381986836337114, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": {}, + "prediction_sha256": "3f816168712d3b935609d80392fe074faac068c4372a1ac25d2267594581301c", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cpu", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 5.201210966000005, + "predict_params_s": 0.8796572620000092, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb" + }, + { + "phase": "repeat_fit", + "fit_s": 3.2026938079999923, + "predict_params_s": 0.6871226760000013, + "metrics": { + "nll": 1.096392042765614, + "crps": 0.40381986829061783, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 0, + "objective_calls": 30, + "trainer_device_to_host_calls": 0 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846408128738403, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341245412826538, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "b237111674ec268f7b73130c0003c7ba2e5e6afb6038ac0800786482386874bb", + "repeat_prediction_max_abs_error": 0.0 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + }, + { + "backend": "cuda", + "seed": 2, + "mode": "eval", + "config": { + "n_trees": 30, + "max_depth": 3, + "learning_rate": 0.05, + "n_bins": 64, + "min_child_weight": 1.0, + "reg_lambda": 1.0, + "subsample": 1.0, + "colsample_bytree": 1.0 + }, + "split_sizes": [ + 12384, + 4128, + 4128 + ], + "records": [ + { + "phase": "first_fit", + "fit_s": 2.509326623000007, + "predict_params_s": 0.016671253000012598, + "metrics": { + "nll": 1.096392041715409, + "crps": 0.403819866890205, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.201305627822876, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.1229389905929565, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "9af00eccc01727765f72dde3b81530643aa5427a31ade9b25451d0dcdf00f7cc" + }, + { + "phase": "repeat_fit", + "fit_s": 0.23239498899999944, + "predict_params_s": 0.01806297500000653, + "metrics": { + "nll": 1.0963920449466849, + "crps": 0.4038198678278385, + "coverage90": 0.9643895348837209 + }, + "fit_path": { + "native_tree_calls": 60, + "objective_calls": 30, + "trainer_device_to_host_calls": 60 + }, + "fallback_warnings": [], + "eval_history": { + "eval_0": { + "nll": [ + 1.5312166213989258, + 1.495821237564087, + 1.4672023057937622, + 1.4427677392959595, + 1.4215803146362305, + 1.4020870923995972, + 1.3846409320831299, + 1.3677259683609009, + 1.3514151573181152, + 1.3365565538406372, + 1.3223012685775757, + 1.3079921007156372, + 1.2950739860534668, + 1.2821121215820312, + 1.269788146018982, + 1.2575006484985352, + 1.245883822441101, + 1.2341246604919434, + 1.2225844860076904, + 1.2116706371307373, + 1.2013055086135864, + 1.1904112100601196, + 1.1799663305282593, + 1.170041561126709, + 1.1602288484573364, + 1.1504559516906738, + 1.140580177307129, + 1.1319608688354492, + 1.122938871383667, + 1.1140406131744385 + ] + } + }, + "prediction_sha256": "a5e059e04cdc02fa03b1f05dde2cd3b5264ca34bf3ca4f27fd6d86ae372fa712", + "repeat_prediction_max_abs_error": 4.76837158203125e-07 + } + ], + "timing_scope": "First/repeat fit within fresh Python process, fresh NUMBA_CACHE_DIR; includes binning, gradients, transfers and JIT; excludes imports/data loading/startup. Prediction includes test binning. Path instrumentation included.", + "transfer_scope": "Counts only trainer._to_host device arguments during fit; excludes compact tree conversion and backend internal copies; not a total transfer count.", + "worker_stderr": "" + } + ], + "unsupported_comparisons": [ + "strict CUDA eval/callbacks" + ], + "value_summary": { + "per_seed": [ + { + "seed": 0, + "quality_pass": true, + "legacy_matches_frozen_quality": true, + "warm_fit_ratio": 12.934523466982723 + }, + { + "seed": 1, + "quality_pass": true, + "legacy_matches_frozen_quality": true, + "warm_fit_ratio": 12.555029690681348 + }, + { + "seed": 2, + "quality_pass": true, + "legacy_matches_frozen_quality": true, + "warm_fit_ratio": 12.917293759831116 + } + ], + "warm_fit_medians_s": { + "legacy_cpu": 2.347555104999998, + "legacy_cuda": 0.14493832599998768, + "experimental_cuda": 1.8680187150000052, + "extensions_cuda": 1.8894675399999983 + }, + "default_fit_ratio": 12.888369602117274, + "profiling_triggered": true, + "quality_pass": true, + "performance_budget_pass": false, + "scope": "resident fit only; eval/callbacks unsupported on strict GPU; T4 seconds are not billed dollars" + } + }, + "remote_function_wall_s": 294.95058564600004, + "timing_scope": "suite execution including environment checks/JIT; excludes image/startup, not billed duration; baseline cell timings have separate scopes" +} diff --git a/benchmarks/results/foundation/p6-cpu-3f8addd/README.md b/benchmarks/results/foundation/p6-cpu-3f8addd/README.md new file mode 100644 index 0000000..ac88d8d --- /dev/null +++ b/benchmarks/results/foundation/p6-cpu-3f8addd/README.md @@ -0,0 +1,58 @@ +# P6 CPU independent-wheel conformance + +Clean source: `3f8addda5a6c9494164b5e69923538f0bf33b56b`. + +**5 passed / 0 skipped**, plus the executed weighted 64-row public demo and +**6 exact CPU prediction roundtrips after uninstalling both extension packages** +in a fresh interpreter. JUnit records the five independent math/composition +cases. This is CPU conformance, not an end-to-end performance benchmark. + +Three wheels were built and installed into a disposable fresh venv outside the +repository. Module paths resolve under that venv's site-packages, with neither +editable installation nor PYTHONPATH injection. Source imports of the two method +packages use only NumPy and the public openboost.experimental module. The Normal +objective/schedule occupies 89 source lines and the bounded leaf 22 (including +blank lines/docstrings). No core change was required: the OpenBoost wheel hash +is identical to the P4.4 wheel. + +Wheel SHA256: + +- OpenBoost: `46dec4c697a5dfa21d8c6bca7e17a26019fd3c3373f0dd2ad539eed3ff819450` +- normal_fisher: `7024337a14fe927b9dfc830d619fa9b9faabc917c3ca0f0ba89adcf134ff9b88` +- bounded_leaves: `3f19f3d600c798e12d2c759ccd480c2e431359399a2136868333253ac4e0a6e6` + +Independent finite-difference NLL verifies weighted gradients; analytic Fisher +checks effective curvature. Exhaustive original-row depth-one reference checks +two rounds of tree predictions and channel updates. Separate and combined fits +establish that schedule and clipping change predictions, and clipping changes +the next mean gradient. An opposing-target validation fixture triggers genuine +early stopping and restores both trees and coefficients to the first round. + +The demo uses seed 7, 64 rows, nonuniform/zero weights, two rounds and depth two. +Its actual coefficients are mu=[0.2,0.1], log_sigma=[0.1,0.05]. Dataset hash and +checks are recorded in results.json. Saved raw predictions are reproduced +exactly without either extension importable for all four composition variants, +the early-stopped model and the weighted demo. + +Environment: Python 3.12.12, Intel macOS x86_64, CPU thread settings=1, +NumPy 2.2.6, Numba 0.61.2, llvmlite 0.44.0, SciPy 1.15.3. Full package pins, +uv version, source/lock hashes and installed paths are recorded in results.json. +The initial attempt with development Numba 0.63.1 / llvmlite 0.46.0 failed in a +source build on this platform. The committed example requirements select an +installable binary stack satisfying OpenBoost's unchanged dependency ranges. +That setup obstacle is part of the result, not a cross-platform compatibility claim. + +Reproduce from the clean source revision (development environment provides uv): + +```sh +uv run --no-sync python examples/extensions/verify_wheels.py /tmp/openboost-extension-evidence +``` + +The recorded final run used UV_OFFLINE=1 after the fixed public dependencies had +been cached. Source file hashes were independently checked against git objects; +JUnit has exactly five passing cases and no failures/errors/skips. Temporary +paths and hostname are omitted from committed evidence. + +Boundary: both example packages declare CPU only. P5 strict GPU trainer +integration and real-device package conformance remain open. These are +repository-authored examples, not third-party adoption or algorithm novelty. diff --git a/benchmarks/results/foundation/p6-cpu-3f8addd/junit.xml b/benchmarks/results/foundation/p6-cpu-3f8addd/junit.xml new file mode 100644 index 0000000..ce709cc --- /dev/null +++ b/benchmarks/results/foundation/p6-cpu-3f8addd/junit.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/benchmarks/results/foundation/p6-cpu-3f8addd/results.json b/benchmarks/results/foundation/p6-cpu-3f8addd/results.json new file mode 100644 index 0000000..c33ed9b --- /dev/null +++ b/benchmarks/results/foundation/p6-cpu-3f8addd/results.json @@ -0,0 +1,91 @@ +{ + "source_sha": "3f8addda5a6c9494164b5e69923538f0bf33b56b", + "source_dirty": false, + "files": { + "examples/extensions/README.md": "ddbd8c6e61a25cba1244909ea73ef4e46e1b11e5c5680ca837e9514e275a6655", + "examples/extensions/bounded_leaves/README.md": "45fa7deb9c04fcf980d495bd983847db5bce8eb3434741b0773ff7fe3d88c1e5", + "examples/extensions/bounded_leaves/pyproject.toml": "c2084f4993b9ecc9a9b82912b02d35ad14057cba062b35cd65081d7bd627a956", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "bd51b4fc99fa8aa85355a5c2dc9583c48584ebc47dd383b8a06e886a0dc9f000", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "1ef335865a55b7b8d0b1e09fabe98cb1e5dbbfd17f9367be7783ce1ab00323d1", + "examples/extensions/check_inference.py": "f6edb46b17eba330044405e894185b2be2161ac304b9a7e60a6d9489d879d02f", + "examples/extensions/demo.py": "69bbd98813e4975b7da85d5a9b2aaa0e4b833b311b83cc7c438020b37bd2759b", + "examples/extensions/normal_fisher/README.md": "19b5ddc4905d0322cab5710ba721e592371bc4d39913e74fb412e56bd081c925", + "examples/extensions/normal_fisher/pyproject.toml": "59c5e7b8a1d2f6280fa4d6e9b6710c906e56d6c65d2158cf111c1cb07e966992", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "be28cd7cbd49ccd093005095f9527001a8d2fbf22877230a86b3540c216186ac", + "examples/extensions/normal_fisher/tests/test_normal.py": "1d2f2b1bc3a6bad8cb7ebf18ab6df19e389c9107bd5fc3bf0fd2e04be0a09427", + "examples/extensions/requirements-cpu.txt": "1f15e5d979db2378977adbdfa06ec08f60e5772d17c6bf9dfcd8b27a10f0c5c6", + "examples/extensions/test_composition.py": "a2b2b0e6fc99ba850846c4a2b1e91a7d2850326e383edc3005cefd93edf3e9c6", + "examples/extensions/verify_wheels.py": "4579f811882996a67e87908170765bd852d590af1343e8ea7ffa1a1da47e431a" + }, + "wheel_hashes": { + "openboost-1.0.0rc1-py3-none-any.whl": "46dec4c697a5dfa21d8c6bca7e17a26019fd3c3373f0dd2ad539eed3ff819450", + "openboost_example_bounded_leaves-0.1.0-py3-none-any.whl": "3f19f3d600c798e12d2c759ccd480c2e431359399a2136868333253ac4e0a6e6", + "openboost_example_normal_fisher-0.1.0-py3-none-any.whl": "7024337a14fe927b9dfc830d619fa9b9faabc917c3ca0f0ba89adcf134ff9b88" + }, + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "python": "3.12.12", + "os": "Darwin", + "machine": "x86_64", + "threads": 1, + "uv_version": "uv 0.12.1 (329541a50 2026-07-31 aarch64-apple-darwin)", + "packages": [ + "Pygments==2.19.2", + "iniconfig==2.3.0", + "joblib==1.5.3", + "llvmlite==0.44.0", + "numba==0.61.2", + "numpy==2.2.6", + "openboost-example-bounded-leaves==0.1.0", + "openboost-example-normal-fisher==0.1.0", + "openboost==1.0.0rc1", + "packaging==25.0", + "pluggy==1.6.0", + "pytest==9.0.2", + "scipy==1.15.3" + ], + "module_paths_relative_to_venv": { + "openboost": "lib/python3.12/site-packages/openboost/__init__.py", + "normal_fisher": "lib/python3.12/site-packages/normal_fisher/__init__.py", + "bounded_leaves": "lib/python3.12/site-packages/bounded_leaves/__init__.py" + }, + "extension_openboost_imports": [ + "openboost.experimental" + ], + "method_source_lines": { + "bounded_leaves": 22, + "normal_fisher": 89 + }, + "inference_after_uninstall": { + "extensions_absent": true, + "exact_cpu_roundtrips": 6 + }, + "demo": { + "data_sha256": "30edad3a2fe4867d52eaf13dcf9742e241d4b4874a448bb4dc0ea617b75dfcf1", + "samples": 64, + "seed": 7, + "coefficients": { + "mu": [ + 0.2, + 0.1 + ], + "log_sigma": [ + 0.1, + 0.05 + ] + }, + "finite_positive_scale": true + }, + "composition": { + "models": 5, + "early_best_iteration": 0, + "channels": [ + "mu", + "log_sigma" + ], + "seed": 7, + "samples": 4, + "two_round_oracle": true + }, + "command": "uv run --no-sync python examples/extensions/verify_wheels.py OUTPUT", + "scope": "CPU installation and mathematical conformance; no external adoption or GPU claim" +} diff --git a/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/README.md b/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/README.md new file mode 100644 index 0000000..39f8050 --- /dev/null +++ b/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/README.md @@ -0,0 +1,20 @@ +# Fixed-slot growth: independent CPU wheel conformance + +Clean source `a5bf26d3d54374e4c524375a02294f0c64abbc1c`. Fresh wheel installation +outside the repository: **7 passed**, standalone weighted demo passed, six exact +CPU predictions after uninstalling both plugins and starting a new interpreter. +The core wheel is identical to the T4 correctness and P7 value bundles: +`8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077`. +Both 0.2.0 extension wheels remain unchanged from P6. + +Tests cover independent Normal NLL/Fisher mathematics, bounded leaf/schedule +composition, subsequent gradients, early stopping and persistence. Public-only +imports, source hashes, actual installed paths and environment are in results. +Darwin x86_64 / Python 3.12.12 / NumPy 2.2.6 / Numba .61.2 / one thread; CPU only. +This is conformance, not independent adoption or a speed benchmark. + +```sh +UV_OFFLINE=1 uv run --no-sync python examples/extensions/verify_wheels.py OUTPUT +``` + +Offline execution used already-cached dependencies; no package publishing. diff --git a/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/junit.xml b/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/junit.xml new file mode 100644 index 0000000..7149c67 --- /dev/null +++ b/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/junit.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/results.json b/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/results.json new file mode 100644 index 0000000..93e319b --- /dev/null +++ b/benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/results.json @@ -0,0 +1,92 @@ +{ + "source_sha": "a5bf26d3d54374e4c524375a02294f0c64abbc1c", + "source_dirty": false, + "files": { + "examples/extensions/README.md": "b6e1a624e514db03e67e0951c10f787806537b7eddf616322842b5f21b680511", + "examples/extensions/bounded_leaves/README.md": "749f68fafb9e8cb5911c16aa79ab7a82593599e5c196c20be3ec9923172144e9", + "examples/extensions/bounded_leaves/pyproject.toml": "8731c3fa03f03341fcc987b449c04658c5bde3e7a4d24772e3bc2199ceb55eac", + "examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py": "d53927c63c71ea3cf37083e0d71d11aff58941dbed1df430838c2d6b2912efff", + "examples/extensions/bounded_leaves/tests/test_leaf.py": "41339c49f146181811dd0680d2b1abc563448ce35b4a79e8d4f0559c5d949baf", + "examples/extensions/check_inference.py": "f6edb46b17eba330044405e894185b2be2161ac304b9a7e60a6d9489d879d02f", + "examples/extensions/demo.py": "3eb0e67f0f4cd4f30283ff93a3b1a15a6aa6a8d24fd31115888ad45bcac1b8e3", + "examples/extensions/normal_fisher/README.md": "78a5142eaf92a81df9c5908861a1ce66e5ae8b03bf753e0b84a834c5f2a5d0eb", + "examples/extensions/normal_fisher/pyproject.toml": "ab70495fafa430dfb1377a873cbb821c9484c9008cea26575fa3c3fa2a91e40d", + "examples/extensions/normal_fisher/src/normal_fisher/__init__.py": "cabafae48d9ad251eea62230b1d38ba7407fbb113dec116b3e840c05e032afd3", + "examples/extensions/normal_fisher/tests/test_normal.py": "6dcb48f9434879b054fe9a9e8b6fca5f4ba01782d8c45ed4c566a8306ee867c3", + "examples/extensions/requirements-cpu.txt": "1f15e5d979db2378977adbdfa06ec08f60e5772d17c6bf9dfcd8b27a10f0c5c6", + "examples/extensions/test_composition.py": "a2b2b0e6fc99ba850846c4a2b1e91a7d2850326e383edc3005cefd93edf3e9c6", + "examples/extensions/verify_wheels.py": "0f9e75aa247625a4b1198f5a0c6a13953ab23f5c12c64e754a4a0289afe26b20" + }, + "wheel_hashes": { + "openboost-1.0.0rc1-py3-none-any.whl": "8c94b62773f6541a983280b5970642de46c60a35e2bed0fcdf62e9d4a9cb3077", + "openboost_example_bounded_leaves-0.2.0-py3-none-any.whl": "dc236c0f207f7fdf0a52e6bdb8e7330f1ffbb6f63e082bb5226c49294a0f02c3", + "openboost_example_normal_fisher-0.2.0-py3-none-any.whl": "47ddff3a87c85b3f4bb187d7b53b650180a6072983a20bc48f0fb8d379da4e1b" + }, + "uv_lock_sha256": "076f55ce347cae1071c902021b0e7b1b7ab4eec7eab4d95ad2a8f177d8cc9ca1", + "python": "3.12.12", + "os": "Darwin", + "machine": "x86_64", + "threads": 1, + "uv_version": "uv 0.12.1 (329541a50 2026-07-31 aarch64-apple-darwin)", + "packages": [ + "Pygments==2.19.2", + "iniconfig==2.3.0", + "joblib==1.5.3", + "llvmlite==0.44.0", + "numba==0.61.2", + "numpy==2.2.6", + "openboost-example-bounded-leaves==0.2.0", + "openboost-example-normal-fisher==0.2.0", + "openboost==1.0.0rc1", + "packaging==25.0", + "pluggy==1.6.0", + "pytest==9.0.2", + "scipy==1.15.3" + ], + "module_paths_relative_to_venv": { + "openboost": "lib/python3.12/site-packages/openboost/__init__.py", + "normal_fisher": "lib/python3.12/site-packages/normal_fisher/__init__.py", + "bounded_leaves": "lib/python3.12/site-packages/bounded_leaves/__init__.py" + }, + "extension_openboost_imports": [ + "openboost.experimental" + ], + "method_source_lines": { + "bounded_leaves": 22, + "normal_fisher": 122 + }, + "inference_after_uninstall": { + "extensions_absent": true, + "exact_cpu_roundtrips": 6 + }, + "demo": { + "data_sha256": "30edad3a2fe4867d52eaf13dcf9742e241d4b4874a448bb4dc0ea617b75dfcf1", + "device": "cpu", + "samples": 64, + "seed": 7, + "coefficients": { + "mu": [ + 0.2, + 0.1 + ], + "log_sigma": [ + 0.1, + 0.05 + ] + }, + "finite_positive_scale": true + }, + "composition": { + "models": 5, + "early_best_iteration": 0, + "channels": [ + "mu", + "log_sigma" + ], + "seed": 7, + "samples": 4, + "two_round_oracle": true + }, + "command": "uv run --no-sync python examples/extensions/verify_wheels.py OUTPUT", + "scope": "CPU installation and mathematical conformance; no external adoption or GPU claim" +} diff --git a/benchmarks/scoringbench/README.md b/benchmarks/scoringbench/README.md new file mode 100644 index 0000000..30453fd --- /dev/null +++ b/benchmarks/scoringbench/README.md @@ -0,0 +1,173 @@ +# OpenBoost on ScoringBench + +[ScoringBench](https://github.com/jonaslandsgesell/ScoringBench) is an external +benchmark for probabilistic regression. It evaluates complete predictive +distributions with proper scoring rules and publishes accepted results at +[scoringbench.com](https://scoringbench.com/). This integration uses its dataset +loader, folds, metrics and Parquet schema without modifying the checkout. + +This is the primary third-party value benchmark for OpenBoost. It answers two +different questions with two deliberately separate protocols: + +1. **Official quality track**: ScoringBench's default 3,000-row cap and full + dataset suite. These results can be proposed for its public leaderboard. +2. **Scale extension**: selected ScoringBench datasets with a larger or removed + row cap. This measures OpenBoost's CPU/CUDA scaling but must not be presented + as an official ScoringBench leaderboard result. + +## Environment + +Use a separate Linux environment because ScoringBench currently constrains +NumPy to `>=2,<2.3` and imports PyTorch for its metrics. Intel macOS is not +supported by the complete launcher: the available PyTorch wheel uses the NumPy +1.x ABI and can crash with ScoringBench's NumPy 2.x requirement. Published CPU +and CUDA measurements should come from Linux in any case. The smaller wrapper +contract test remains useful for local adapter development. + +```bash +git clone https://github.com/jonaslandsgesell/ScoringBench .repos/ScoringBench + +uv venv .venv-scoringbench --python 3.12 +uv pip install --python .venv-scoringbench/bin/python \ + -r benchmarks/scoringbench/requirements.txt +uv pip install --python .venv-scoringbench/bin/python -e . +``` + +For CUDA, install OpenBoost's CUDA extra using the package versions appropriate +for the benchmark machine: + +```bash +uv pip install --python .venv-scoringbench/bin/python -e '.[cuda]' +``` + +Optional comparison models: + +```bash +uv pip install --python .venv-scoringbench/bin/python xgboostlss catboost +``` + +## Validate the adapter + +This uses one existing sklearn dataset and the complete ScoringBench metrics, +but is only an integration smoke test: + +```bash +.venv-scoringbench/bin/python benchmarks/scoringbench/run.py \ + --scoringbench-dir .repos/ScoringBench \ + --models openboost_cpu,ngboost \ + --smoke \ + --n-trees 20 \ + --output-dir /tmp/openboost-scoringbench-smoke +``` + +The smaller wrapper contract test can also be run directly: + +```bash +PYTHONPATH=.repos/ScoringBench \ + .venv-scoringbench/bin/python -m pytest \ + benchmarks/scoringbench/test_openboost_wrapper.py -q +``` + +## Official quality track + +Run the official default: five folds, one repeat, at most 3,000 rows per +dataset. Start with OpenBoost and the existing NGBoost wrapper: + +```bash +.venv-scoringbench/bin/python benchmarks/scoringbench/run.py \ + --scoringbench-dir .repos/ScoringBench \ + --models openboost_cpu,ngboost \ + --sample-size 3000 \ + --n-folds 5 \ + --n-repeats 1 \ + --output-dir benchmarks/results/scoringbench-quality +``` + +Use `--dataset-index N` or `--dataset-name NAME` for resumable shards. Use +`--list-datasets` to display the validated list. Do not tune OpenBoost on the +test folds. If hyperparameters are changed, apply the same declared search +budget to every comparison model. + +After all shards complete, run ScoringBench's own aggregation and autoranking: + +```bash +cd .repos/ScoringBench +python aggregate_datasets.py \ + --raw_dir ../../benchmarks/results/scoringbench-quality/raw \ + --out_dir ../../benchmarks/results/scoringbench-quality +python autorank_leaderboard.py --output_dir ../../benchmarks/results/scoringbench-quality +``` + +For an upstream submission, copy `openboost_wrapper.py` into +`scoringbench/wrappers/`, register `OpenBoostWrapper` in the upstream wrapper +exports and add a zero-argument factory to its `MODELS` dictionary. Submit the +wrapper, raw/aggregated Parquet artifacts and leaderboard JSON for independent +review. + +## ScoringBench scale extension + +First identify large datasets from the official list, then run the same folds +and metrics without the 3,000-row cap. CPU and CUDA are separate model names so +their results cannot be confused: + +```bash +.venv-scoringbench/bin/python benchmarks/scoringbench/run.py \ + --scoringbench-dir .repos/ScoringBench \ + --models openboost_cpu,openboost_cuda,ngboost \ + --dataset-name '' \ + --sample-size 0 \ + --n-folds 5 \ + --output-dir benchmarks/results/scoringbench-scale +``` + +Run each CUDA measurement in a fresh process. Report cold and repeated runs +separately, and include failures/OOMs. The generated `openboost_manifest.json` +records both git commits, dirty state, arguments, package versions, platform and +GPU identity. Runs whose manifest says `scoringbench_scale_extension` are not +official leaderboard runs. + +Generated ScoringBench directories are gitignored. Publish accepted evidence in +ScoringBench's designated output/LFS repository or intentionally force-add a +frozen artifact; do not commit an arbitrary local smoke run. + +## Evidence gate + +OpenBoost should claim value only after all of the following are true: + +- the wrapper and results are accepted upstream by ScoringBench; +- quality is reported across the full suite, not a selected winning subset; +- paired fold-level CRPS/log-score/interval-score differences include + uncertainty intervals or the upstream statistical ranking; +- CPU and CUDA predictions pass a separate parity gate; +- a scale curve uses at least three real datasets and multiple data sizes; +- a speed claim is made only at matched predictive quality, with raw Parquet + files and `openboost_manifest.json` published. + +The benchmark is allowed to disprove the product hypothesis. If OpenBoost is +not competitive on proper scoring rules or does not accelerate at larger row +counts, the result should be published and the implementation fixed before the +README makes a performance claim. + +## Declared parameters and current evidence boundary + +The launcher passes `--seed` to OpenBoost, NGBoost, XGBLSS and CatBoost. For +NGBoost, each factory creates a fresh clone of the installed default tree learner +and applies `--max-depth` and the seed, preserving its other installed defaults. +The manifest's `model_parameters` records constructed wrapper parameters and +base-learner settings; CLI arguments alone are not proof of applied parameters. +Equal round/depth settings do not imply equal computation across algorithms. + +Local parameter-contract tests run without the complete upstream metrics stack: + +```sh +OPENBOOST_BACKEND=cpu uv run pytest tests/test_scoringbench_config.py -n 0 -q +``` + +They check constructor forwarding, independent base learners, JSON configuration +and actual OpenBoost sampling reproducibility. Optional competitor constructors +and upstream metric imports are isolated in these local tests; they do not verify +full NGBoost fitting or ScoringBench metrics. The complete Linux integration and +quality suite remain outstanding. Before interpreting resumable runs as complete +quality evidence, validate cache/config compatibility and every requested fold, +including dataset/model failures and effective per-dataset row caps. Existing +smoke tests or an output manifest do not establish upstream acceptance. diff --git a/benchmarks/scoringbench/__init__.py b/benchmarks/scoringbench/__init__.py new file mode 100644 index 0000000..ee2d4bb --- /dev/null +++ b/benchmarks/scoringbench/__init__.py @@ -0,0 +1,6 @@ +"""OpenBoost integration for the external ScoringBench benchmark. + +The adapter is not imported here because ScoringBench is an optional external +checkout. Import ``benchmarks.scoringbench.openboost_wrapper`` explicitly after +putting that checkout on ``PYTHONPATH``. +""" diff --git a/benchmarks/scoringbench/openboost_wrapper.py b/benchmarks/scoringbench/openboost_wrapper.py new file mode 100644 index 0000000..afe8822 --- /dev/null +++ b/benchmarks/scoringbench/openboost_wrapper.py @@ -0,0 +1,166 @@ +"""ScoringBench wrapper for OpenBoost NaturalBoost. + +This module intentionally lives in OpenBoost's repository while the integration +is being validated. It is also shaped as an upstream-ready ScoringBench wrapper: +copy it to ``scoringbench/wrappers/openboost_wrapper.py`` and update the upstream +registry when submitting benchmark results. +""" + +from __future__ import annotations + +from contextlib import nullcontext + +import numpy as np + +try: + from scoringbench.wrappers.base import DistributionPrediction, ProbabilisticWrapper + from scoringbench.wrappers.quantile_based import quantiles_to_distribution +except ImportError as exc: # pragma: no cover - depends on the external checkout + raise ImportError( + "OpenBoostWrapper requires a ScoringBench checkout on PYTHONPATH. " + "See benchmarks/scoringbench/README.md." + ) from exc + + +class OpenBoostWrapper(ProbabilisticWrapper): + """OpenBoost NaturalBoost with a Gaussian predictive distribution. + + Parameters mirror ScoringBench's NGBoost Gaussian entry by default: 500 + boosting rounds, learning rate 0.01, depth-3 trees and 99 quantile levels. + The backend is explicit so CPU and CUDA results cannot be accidentally + conflated on a leaderboard. + + Parameters + ---------- + backend: + ``"cpu"``, ``"cuda"`` or ``"auto"``. ``"auto"`` uses OpenBoost's + normal backend detection; reproducible benchmark runs should use an + explicit backend. + n_trees: + Number of NaturalBoost rounds. + learning_rate: + Boosting shrinkage. + max_depth: + Maximum depth of each parameter tree. + n_bins: + Histogram bins. OpenBoost reserves bin 255 for missing values, so 254 + is the largest non-warning value. + n_quantiles: + Number of probability levels used to convert the analytic Normal + distribution into ScoringBench's common PMF representation. + model_params: + Additional keyword arguments forwarded to ``NaturalBoostNormal``. + """ + + _VALID_BACKENDS = {"auto", "cpu", "cuda"} + + def __init__( + self, + *, + backend: str = "cpu", + n_trees: int = 500, + learning_rate: float = 0.01, + max_depth: int = 3, + n_bins: int = 254, + n_quantiles: int = 99, + model_params: dict | None = None, + ) -> None: + backend = backend.lower() + if backend not in self._VALID_BACKENDS: + raise ValueError( + f"backend must be one of {sorted(self._VALID_BACKENDS)}, got {backend!r}" + ) + if n_quantiles < 2: + raise ValueError("n_quantiles must be at least 2") + + self.backend = backend + self.n_trees = n_trees + self.learning_rate = learning_rate + self.max_depth = max_depth + self.n_bins = n_bins + self.n_quantiles = n_quantiles + self.model_params = dict(model_params or {}) + + self._alphas = np.linspace( + 1 / (n_quantiles + 1), + n_quantiles / (n_quantiles + 1), + n_quantiles, + dtype=np.float64, + ) + self._model = None + self._resolved_backend: str | None = None + self._y_range = (0.0, 1.0) + + @staticmethod + def _sanitize_X(X) -> np.ndarray: + X = np.asarray(X, dtype=np.float32) + if X.ndim != 2: + raise ValueError(f"X must be 2-dimensional, got shape {X.shape}") + return np.nan_to_num(X, nan=0.0, posinf=1e7, neginf=-1e7) + + def _backend_context(self): + import openboost as ob + + if self._resolved_backend is None: + return nullcontext() + return ob.backend_context(self._resolved_backend) + + def _require_fitted(self) -> None: + if self._model is None: + raise RuntimeError("Model not fitted. Call fit() first.") + + def fit(self, X, y) -> OpenBoostWrapper: + import openboost as ob + + X = self._sanitize_X(X) + y = np.asarray(y, dtype=np.float32).reshape(-1) + valid = np.isfinite(y) + X, y = X[valid], y[valid] + if len(y) == 0: + raise ValueError("No valid finite training samples") + + lo, hi = float(y.min()), float(y.max()) + if lo == hi: + pad = max(abs(lo) * 1e-6, 1e-7) + lo, hi = lo - pad, hi + pad + self._y_range = (lo, hi) + + self._resolved_backend = ob.get_backend() if self.backend == "auto" else self.backend + params = { + "n_trees": self.n_trees, + "learning_rate": self.learning_rate, + "max_depth": self.max_depth, + "n_bins": self.n_bins, + **self.model_params, + } + self._model = ob.NaturalBoostNormal(**params) + with self._backend_context(): + self._model.fit(X, y) + return self + + def predict(self, X) -> np.ndarray: + self._require_fitted() + X = self._sanitize_X(X) + with self._backend_context(): + pred = self._model.predict(X) + return np.asarray(pred, dtype=np.float64).reshape(-1) + + def predict_distribution(self, X) -> DistributionPrediction: + self._require_fitted() + X = self._sanitize_X(X) + with self._backend_context(): + output = self._model.predict_distribution(X) + mean = np.asarray(output.mean(), dtype=np.float64).reshape(-1) + quantiles = np.column_stack( + [ + np.asarray(output.quantile(float(alpha)), dtype=np.float64) + for alpha in self._alphas + ] + ) + + return quantiles_to_distribution( + quantiles, + self._alphas, + mean=mean, + y_range=self._y_range, + ) diff --git a/benchmarks/scoringbench/requirements.txt b/benchmarks/scoringbench/requirements.txt new file mode 100644 index 0000000..44cf484 --- /dev/null +++ b/benchmarks/scoringbench/requirements.txt @@ -0,0 +1,11 @@ +# Base ScoringBench runtime, deliberately excluding its many heavyweight model +# extras. Install only the additional baselines you plan to run. +numpy>=2.0,<2.3 +scikit-learn>=1.3 +pandas>=2.0 +torch>=2.0 +pyarrow>=15 +autorank>=1.2 +openml>=0.15 +pytest>=7 +ngboost>=0.5 diff --git a/benchmarks/scoringbench/run.py b/benchmarks/scoringbench/run.py new file mode 100644 index 0000000..4b76c03 --- /dev/null +++ b/benchmarks/scoringbench/run.py @@ -0,0 +1,418 @@ +"""Run OpenBoost through an unmodified ScoringBench checkout. + +The official suite owns datasets, folds, metrics and Parquet output. This +launcher only registers OpenBoost (plus selected existing baselines), records +provenance and exposes a small smoke mode for integration testing. +""" + +from __future__ import annotations + +import argparse +import importlib.metadata +import json +import os +import platform +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + +import numpy as np + +PROJECT_ROOT = Path(__file__).resolve().parents[2] +SRC_ROOT = PROJECT_ROOT / "src" + + +def _csv(value: str) -> list[str]: + return [item.strip() for item in value.split(",") if item.strip()] + + +def _git_state(path: Path) -> dict: + def run(*args: str) -> str | None: + try: + result = subprocess.run( + ["git", "-C", str(path), *args], + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + except (OSError, subprocess.CalledProcessError): + return None + + status = run("status", "--porcelain") + return { + "commit": run("rev-parse", "HEAD"), + "dirty": bool(status) if status is not None else None, + } + + +def _package_version(name: str) -> str | None: + try: + return importlib.metadata.version(name) + except importlib.metadata.PackageNotFoundError: + return None + + +def _gpu_info() -> dict | None: + try: + from numba import cuda + + if not cuda.is_available(): + return None + device = cuda.get_current_device() + name = device.name.decode() if isinstance(device.name, bytes) else str(device.name) + return { + "name": name, + "compute_capability": list(device.compute_capability), + } + except Exception as exc: # provenance should never fail the benchmark + return {"error": f"{type(exc).__name__}: {exc}"} + + +def _build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Run OpenBoost on the external ScoringBench protocol" + ) + parser.add_argument( + "--scoringbench-dir", + default=os.environ.get("SCORINGBENCH_DIR", ".repos/ScoringBench"), + help="Path to a ScoringBench git checkout", + ) + parser.add_argument( + "--output-dir", + default="benchmarks/results/scoringbench", + help="ScoringBench Parquet and OpenBoost manifest output", + ) + parser.add_argument( + "--models", + type=_csv, + default=["openboost_cpu", "ngboost"], + help=( + "Comma-separated models: openboost_cpu, openboost_cuda, ngboost, " + "xgblss, catboost_quantile" + ), + ) + parser.add_argument("--n-trees", type=int, default=500) + parser.add_argument("--learning-rate", type=float, default=0.01) + parser.add_argument("--max-depth", type=int, default=3) + parser.add_argument("--n-quantiles", type=int, default=99) + parser.add_argument("--seed", type=int, default=42) + parser.add_argument("--n-folds", type=int, default=5) + parser.add_argument("--n-repeats", type=int, default=1) + parser.add_argument( + "--sample-size", + type=int, + default=3000, + help="Official ScoringBench default is 3000; use 0 only for a scale extension", + ) + parser.add_argument( + "--dataset-index", + type=int, + action="append", + help="Run selected index from ScoringBench's validated dataset list (repeatable)", + ) + parser.add_argument( + "--dataset-name", + action="append", + help="Run exact case-insensitive dataset name from the validated list (repeatable)", + ) + parser.add_argument( + "--lite", + action="store_true", + help="Use two folds while retaining ScoringBench datasets and metrics", + ) + parser.add_argument( + "--smoke", + action="store_true", + help="Use sklearn diabetes with 2 folds; validates integration, not leaderboard evidence", + ) + parser.add_argument( + "--list-datasets", + action="store_true", + help="Print ScoringBench's validated dataset names and exit", + ) + return parser + + +def _select_datasets(all_datasets: list[dict], args) -> list[dict]: + if args.dataset_index: + invalid = [i for i in args.dataset_index if i < 0 or i >= len(all_datasets)] + if invalid: + raise ValueError( + f"dataset indices out of range: {invalid}; valid range is 0..{len(all_datasets) - 1}" + ) + return [all_datasets[i] for i in args.dataset_index] + + if args.dataset_name: + lookup = {dataset["name"].casefold(): dataset for dataset in all_datasets} + missing = [name for name in args.dataset_name if name.casefold() not in lookup] + if missing: + raise ValueError(f"unknown dataset names: {missing}; use --list-datasets") + return [lookup[name.casefold()] for name in args.dataset_name] + + return all_datasets + + +def _model_factories(args): + from benchmarks.scoringbench.openboost_wrapper import OpenBoostWrapper + + common = { + "n_trees": args.n_trees, + "learning_rate": args.learning_rate, + "max_depth": args.max_depth, + "n_quantiles": args.n_quantiles, + } + + def openboost(backend: str): + return lambda: OpenBoostWrapper(backend=backend, model_params={"random_state": args.seed}, **common) + + factories = { + "openboost_cpu": openboost("cpu"), + "openboost_cuda": openboost("cuda"), + } + + if "ngboost" in args.models: + from ngboost.learners import default_tree_learner + from scoringbench.wrappers.ngboost_wrapper import NGBoostWrapper + from sklearn.base import clone + + factories["ngboost"] = lambda: NGBoostWrapper( + dist="normal", + n_estimators=args.n_trees, + learning_rate=args.learning_rate, + n_quantiles=args.n_quantiles, + ngb_params={ + "random_state": args.seed, + "Base": clone(default_tree_learner).set_params( + max_depth=args.max_depth, random_state=args.seed + ), + }, + ) + + if "xgblss" in args.models: + from scoringbench.wrappers.xgblss_wrapper import XGBLSSWrapper + + factories["xgblss"] = lambda: XGBLSSWrapper( + n_quantiles=args.n_quantiles, + num_boost_round=args.n_trees, + distribution="Gaussian", + xgblss_params={"max_depth": args.max_depth, "eta": args.learning_rate, "seed": args.seed}, + ) + + if "catboost_quantile" in args.models: + from scoringbench.wrappers.catboost_wrapper import CatBoostQuantileWrapper + + factories["catboost_quantile"] = lambda: CatBoostQuantileWrapper( + n_quantiles=args.n_quantiles, + iterations=args.n_trees, + catboost_params={ + "depth": args.max_depth, + "learning_rate": args.learning_rate, + "random_seed": args.seed, + }, + ) + + valid = set(factories) + unknown = [name for name in args.models if name not in valid] + if unknown: + allowed = [ + "openboost_cpu", + "openboost_cuda", + "ngboost", + "xgblss", + "catboost_quantile", + ] + raise ValueError(f"unknown models {unknown}; allowed values: {allowed}") + + return {name: factories[name] for name in args.models} + + +def _model_configuration(model): + """Record constructed wrapper parameters, including the cloned base learner. + + This is configuration evidence, not proof of execution device or completion. + Exclude private fitted state and avoid lossy string representations. + """ + def encode(value): + if value is None or isinstance(value, (str, bool, int, float)): + return value + if isinstance(value, np.generic): + return value.item() + if isinstance(value, np.ndarray): + return value.tolist() + if isinstance(value, (list, tuple)): + return [encode(v) for v in value] + if isinstance(value, dict): + return {k: encode(v) for k, v in value.items()} + if hasattr(value, "get_params"): + return {"class": f"{type(value).__module__}.{type(value).__name__}", + "parameters": encode(value.get_params(deep=False))} + raise TypeError(f"Unrecordable model configuration: {type(value).__name__}") + + return {"wrapper": f"{type(model).__module__}.{type(model).__name__}", + "parameters": encode({k: v for k, v in vars(model).items() if not k.startswith("_")})} + + +def _write_provenance( + output_dir: Path, + scoringbench_dir: Path, + args, + datasets: list[dict], + result_rows: int, + model_parameters: dict, +) -> Path: + import openboost as ob + + official_shape = ( + not args.smoke + and args.sample_size == 3000 + and args.n_folds == 5 + and args.n_repeats == 1 + ) + if args.smoke: + protocol_mode = "smoke" + elif args.sample_size != 3000: + protocol_mode = "scoringbench_scale_extension" + elif official_shape and (args.dataset_index or args.dataset_name): + protocol_mode = "official_quality_shard" + elif official_shape: + protocol_mode = "official_quality" + else: + protocol_mode = "scoringbench_protocol_deviation" + + manifest = { + "schema_version": 1, + "created_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), + "protocol": "ScoringBench", + "protocol_mode": protocol_mode, + "official_protocol_compatible": official_shape, + "warning": ( + None + if official_shape + else "This run is not directly comparable to the official 5-fold, sample_size=3000 leaderboard." + ), + "openboost_git": _git_state(PROJECT_ROOT), + "scoringbench_git": _git_state(scoringbench_dir), + "arguments": vars(args), + "model_parameters": model_parameters, + "datasets": [ + { + "name": dataset["name"], + "source": dataset.get("source", "openml"), + "id": dataset.get("id", dataset.get("loader")), + } + for dataset in datasets + ], + "result_rows": result_rows, + "platform": { + "python": platform.python_version(), + "system": platform.system(), + "release": platform.release(), + "machine": platform.machine(), + "processor": platform.processor(), + "cpu_count": os.cpu_count(), + "gpu": _gpu_info(), + }, + "versions": { + "openboost": ob.__version__, + "numpy": np.__version__, + **{ + name: _package_version(name) + for name in ( + "scipy", + "scikit-learn", + "pandas", + "pyarrow", + "torch", + "numba", + "numba-cuda", + "cupy-cuda12x", + "ngboost", + "xgboost", + "xgboostlss", + "catboost", + ) + }, + }, + } + output_dir.mkdir(parents=True, exist_ok=True) + path = output_dir / "openboost_manifest.json" + path.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n") + return path + + +def main() -> int: + args = _build_parser().parse_args() + if sys.platform == "darwin" and platform.machine() == "x86_64": + raise SystemExit( + "The complete ScoringBench runner is unsupported on Intel macOS: " + "the available PyTorch wheel uses the NumPy 1.x ABI while " + "ScoringBench requires NumPy 2.x. Run the benchmark on Linux " + "(the target environment for published CPU/CUDA results). The " + "wrapper contract test can still be run separately." + ) + scoringbench_dir = Path(args.scoringbench_dir).expanduser().resolve() + if not (scoringbench_dir / "scoringbench" / "runner.py").exists(): + raise SystemExit( + f"No ScoringBench checkout found at {scoringbench_dir}. " + "Clone https://github.com/jonaslandsgesell/ScoringBench first." + ) + + sys.path.insert(0, str(SRC_ROOT)) + sys.path.insert(0, str(PROJECT_ROOT)) + sys.path.insert(0, str(scoringbench_dir)) + + from scoringbench.datasets import get_DATASETS_CONFIG, validate_datasets + from scoringbench.runner import run_benchmark + from scoringbench.utils import set_seed + + set_seed(args.seed) + if args.smoke: + datasets = [ + { + "name": "diabetes_smoke", + "source": "sklearn", + "loader": "load_diabetes", + "abbr": "DBS", + "sample_size": min(args.sample_size or 442, 442), + } + ] + args.n_folds = 2 + else: + datasets = validate_datasets(get_DATASETS_CONFIG()) + if args.list_datasets: + for index, dataset in enumerate(datasets): + print(f"{index:3d} {dataset['name']}") + return 0 + datasets = _select_datasets(datasets, args) + + if args.lite: + args.n_folds = 2 + + model_factories = _model_factories(args) + model_parameters = {name: _model_configuration(make()) for name, make in model_factories.items()} + output_dir = Path(args.output_dir).expanduser().resolve() + result = run_benchmark( + datasets_config=datasets, + model_factories=model_factories, + output_dir=output_dir, + n_folds=args.n_folds, + n_repeats_cv=args.n_repeats, + seed=args.seed, + sample_size=args.sample_size, + ) + manifest = _write_provenance( + output_dir, + scoringbench_dir, + args, + datasets, + result_rows=len(result), + model_parameters=model_parameters, + ) + print(f"OpenBoost provenance: {manifest}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/scoringbench/test_openboost_wrapper.py b/benchmarks/scoringbench/test_openboost_wrapper.py new file mode 100644 index 0000000..a9c7127 --- /dev/null +++ b/benchmarks/scoringbench/test_openboost_wrapper.py @@ -0,0 +1,33 @@ +"""Upstream-style smoke test for the OpenBoost ScoringBench wrapper.""" + +import numpy as np +from scoringbench.wrappers.base import DistributionPrediction + +from benchmarks.scoringbench.openboost_wrapper import OpenBoostWrapper + + +def test_openboost_wrapper_distribution_contract(): + rng = np.random.default_rng(42) + X = rng.normal(size=(160, 4)).astype(np.float32) + sigma = 0.25 + np.abs(X[:, 1]) + y = (2 * X[:, 0] - X[:, 2] + rng.normal(scale=sigma)).astype(np.float32) + + model = OpenBoostWrapper( + backend="cpu", + n_trees=12, + learning_rate=0.05, + max_depth=2, + n_quantiles=15, + ) + returned = model.fit(X[:120], y[:120]) + distribution = model.predict_distribution(X[120:]) + + assert returned is model + assert isinstance(distribution, DistributionPrediction) + assert distribution.probas.shape == (40, 14) + assert distribution.bin_edges.shape == (40, 15) + assert distribution.mean.shape == (40,) + assert np.all(np.isfinite(distribution.probas)) + assert np.all(np.isfinite(distribution.bin_edges)) + assert np.allclose(distribution.probas.sum(axis=1), 1.0) + assert np.all(np.diff(distribution.bin_edges, axis=1) > 0) diff --git a/benchmarks/v1/README.md b/benchmarks/v1/README.md new file mode 100644 index 0000000..d69c5a6 --- /dev/null +++ b/benchmarks/v1/README.md @@ -0,0 +1,650 @@ +# v1 evaluation preparation + +Sprint 011 adds an **artifact integrity judge**, not the full F0.3 runner or E0–E7 +judge. No real dataset manifest, installed baseline capability result, frozen +resource budget or held-out author task is delivered by this slice. + +Run from a repository checkout with the project environment: + +```bash +uv run --no-sync python -m benchmarks.v1.judge /absolute/path/to/run +``` + +The command reads `manifest.json` and `cases.jsonl`, prints a JSON report to stdout, +and exits 0 only for `integrity_pass: true`; otherwise it exits 1. It does not train, +execute artifact code or modify the run directory. `gate_results` is always empty. +A producer's `pass` is a status claim, never an independently verified quality score. + +## Integrity schema: `openboost-integrity-v0` + +Objects use exactly the documented fields; unknown versions/fields are rejected. +JSON duplicate keys, non-finite numbers and blank JSONL records are rejected. + +Manifest fields: + +- `schema`: the version above; `protocol_sha256`: lowercase SHA-256 of the declared protocol. +- `provenance`: `code_sha` (40 lowercase hex), `dirty` (must be false in this version), + `environment` (nonempty JSON object). Dirty code needs a patch digest in a future + version; a boolean alone cannot identify different uncommitted implementations. +- `expected`: nonempty list of cells. Each has `id`, `application` (`A1`–`A13`), + `required` (boolean), `backend` (`cpu`/`cuda`), `model`, `fold` (nonempty strings), + `seed` (nonnegative integer), `dataset_sha256`, `split_sha256`, + `preprocessing_sha256`, and `config` (JSON object). + +IDs must be unique; every A-ID needs a required CPU cell. This only checks the +**declared matrix**. The future frozen protocol must independently define actual +fold counts, methods, datasets, budget and device requirements; one cell per A-ID +is not a sufficient experimental design. This schema does not yet validate +package/wheel metadata, real data hashes, license, row IDs, target units, resource +limits or the authenticity of recorded provenance. + +Each JSONL case has exactly: + +- `id`, `status` (`not_run/pass/fail/unsupported/error/timeout`), `cache_key`; +- `backend` (`cpu/cuda/none`), `fallback` (boolean), `exit_code` (integer or null); +- `artifacts`, `metrics` (finite numeric values by metric name), `reason` (string). + +`cache_key(manifest, cell)` hashes the canonical JSON of the **entire manifest and +cell**. Code, environment, protocol, matrix, data, split, preprocessing, config and +seed changes invalidate old keys. This conservative version has no cross-matrix +cache reuse. It cannot detect an input that a producer failed to declare. + +All cells, including optional cells, must have a record and a hashed `log` artifact. +Required non-pass records fail integrity. Optional non-pass records remain visible +with a nonempty reason; they cannot establish GPU or quality success. A pass record +requires exit code 0, the expected backend without fallback, nonempty metrics, and +hashed `predictions`, `model`, `log` artifacts. Artifact entries have `path` (relative +inside the run directory) and `sha256`; path escapes and symlinks outside are rejected. +Model and log bytes are hash-checked but not interpreted. Prediction bytes must be +JSON containing a nonempty rectangular finite numeric array. Matching prediction +rows/output semantics to targets and recomputing metrics belong to the forthcoming +independent evaluators. No dataset data is confused with prediction validation: +legitimate censored-label infinity remains part of the AFT target contract. + +## Verification and next steps + +[Adversarial tests](../../tests/v1/test_artifact_judge.py) generate clearly synthetic +bundles in temporary directories. They exercise missing/duplicate/unknown cases, +input changes with stale cache keys, false pass claims, worker failures, unsupported +GPU, missing/corrupted artifacts, invalid prediction values and the CLI exit code. +Synthetic manifests are test inputs, not committed benchmark results. + +Next F0.3 slices must acquire and hash real datasets, implement their split/target +adapters, smoke-test pinned baselines, freeze resources/configurations and held-out +verifiers, add the execution runner and independent metric/gate evaluation. Full +benchmark runs remain prohibited until actual hashes and budgets are frozen. + +## A5 real data preparation + +[Sprint 012](../../v1-sprints/012-bike-data-freeze.md) freezes the UCI Bike Sharing +hourly archive and five full-date rolling splits in [datasets/bike.json](datasets/bike.json). +Source: Hadi Fanaee-T (2013), [UCI Bike Sharing](https://archive.ics.uci.edu/dataset/275/bike+sharing+dataset), +DOI 10.24432/C5W894, CC BY 4.0. The download has **17,379** hourly rows, versus +17,389 on the UCI page checked on 2026-09-06; archive and CSV hashes identify the +actual data used. Raw data is downloaded locally and is not vendored in Git. + +```bash +curl --fail --location 'https://archive.ics.uci.edu/static/public/275/bike%2Bsharing%2Bdataset.zip' -o /tmp/openboost-v1-bike.zip +uv run --no-sync python -m benchmarks.v1.bike /tmp/openboost-v1-bike.zip --verify benchmarks/v1/datasets/bike.json +``` + +`load_archive` verifies both pinned archive and `hour.csv` hashes before parsing. +`parse_hour` exposes X (float64 calendar columns), y (hourly total count), original +integer row IDs, and ISO dates. No fitted preprocessing is performed. Calendar +values and chronological unique timestamps are checked. All records are retained; +missing hours are not synthesized. IDs/dates support alignment and splitting only. +Observed weather, temperature, humidity, wind and casual/registered counts never +enter X. Numeric/calendar encoding is explicit; later adapters may choose categorical +representations without learning from validation/test. + +For D=731 dates, each origin uses floor(D*p/100), floor(D*(p+10)/100), +floor(D*(p+20)/100) endpoints for p=50,55,60,65,70. Each date stays whole. +Origins overlap; they are not independent trials. The unused final 10% of dates +is intentionally outside the predeclared windows, not an extra selection set. +The freeze contains per-part row counts, date boundaries and row-ID hashes, plus +feature/target array hashes and adapter source hash. `--verify` refuses altered +source or data/splits; replay provenance (revision/environment/argv) may differ. + +This **data preparation record is not an integrity-v0 run manifest**. It honestly +records a dirty checkout and identifies the adapter bytes separately. It does not +freeze training budgets/configurations, run any quantile model, or pass E3/A5. + +## A1/A11 Housing data preparation + +[Sprint 013](../../v1-sprints/013-housing-five-splits.md) adds +[datasets/housing.json](datasets/housing.json). The local archive matches the historical +source hash. The independent v1 adapter exactly reproduces the old X/y hash and +seeds 0–2 split hashes, then adds seeds 3–4 without altering the historical record. + +```bash +curl --fail --location 'https://ndownloader.figshare.com/files/5976036' -o /tmp/openboost-v1-housing.tgz +uv run --no-sync python -m benchmarks.v1.housing /tmp/openboost-v1-housing.tgz --verify benchmarks/v1/datasets/housing.json +``` + +There are 20,640 rows and eight features; each seed has 12,384/4,128/4,128 rows. +Source row positions define IDs. Household ratios are computed per row, and targets +are divided by 100,000 before float32 conversion. No preprocessing is learned from +the complete dataset. The hash format deliberately retains the legacy raw-byte +convention; it differs from Bike's dtype/shape-prefixed hashes and is named explicitly. +Independent tests check column mapping, ratios, units, RNG isolation and partitions. + +A1 regression and A11 Normal distribution predictions share inputs and splits but +require separate scores and acceptance; this counts as one data source. Random +partitions do not establish geographic generalization. The +[scikit-learn description](https://scikit-learn.org/stable/datasets/real_world.html#california-housing-dataset) +provides dataset context. The original freeze recorded an unresolved license. +A subsequent [dated source review](datasets/housing-license-review.json) verifies +that the exact downloaded file matches Figshare's published MD5 and carries its +uploader's CC BY 4.0 declaration. Attribution: Nelson Liu (2016), *scikit-learn +california housing dataset cal_housing.tgz*, +[Figshare version 2](https://doi.org/10.6084/m9.figshare.3829992.v2). Original StatLib +rights provenance was not separately verified. The historical data/preprocessing +freeze is preserved; this review changes no bytes or folds. Model quality is pending. + +## A2 Adult data preparation + +[Adult freeze](datasets/adult.json) preserves official test and creates five +80/20 stratified partitions of official training. Source: +[Becker and Kohavi (1996), UCI Adult](https://archive.ics.uci.edu/dataset/2/adult), +DOI 10.24432/C5XW20, CC BY 4.0. Download and verify with: + +```bash +curl --fail --location 'https://archive.ics.uci.edu/static/public/2/adult.zip' -o /tmp/openboost-v1-adult.zip +uv run --no-sync python -m benchmarks.v1.adult /tmp/openboost-v1-adult.zip --verify benchmarks/v1/datasets/adult.json +``` + +The adapter exposes mixed numeric/string/None records with 13 features; fnlwgt is +excluded, and weights remain unit. Test labels lose exactly one suffix dot. IDs +combine official filename and physical line number. All records remain present. +No fitted categorical encoding is included; subsequent encoding must learn only +from each training partition. Shared predictor values are audited in the artifact +but are not assumed to identify repeated people. The official test is unchanged. + +## Remaining real-data preparation + +`real_data.py` verifies the pinned [source catalog](datasets/sources.json) before +parsing. With the isolated evaluation environment installed: + +```bash +uv venv build/v1-env --python 3.12 +uv pip sync --python build/v1-env/bin/python --require-hashes benchmarks/v1/requirements-cpu.txt +build/v1-env/bin/python -m benchmarks.v1.real_data concrete build/v1-data --verify benchmarks/v1/datasets/concrete.json +``` + +Download each catalog URL into its named file under `build/v1-data/`. Substitute +`covertype`, `parkinsons`, `veteran`, or `insurance` in the replay command. Raw data +is not committed. Hashes include little-endian dtype, shape, and row order. + +- [Covertype](datasets/covertype.json): all 581,012 rows, 54 original features, + seven labels, five stratified splits. The indicators remain separate columns. +- [Parkinsons](datasets/parkinsons.json): 5,875 rows, 19 inputs, two UPDRS targets; + subject IDs determine partitions and cannot enter features. +- [Concrete](datasets/concrete.json): 1,030 rows, seven material inputs; identical + recipes stay together. Age/28 is a separate structural input and MPa is the target. +- [Veteran](datasets/veteran.json): 137 rows, six inputs; observed times and event + indicators stay separate. Source-declared categorical orders are fixed mappings, + not a vocabulary learned from held-out rows. IPCW support preparation remains open. +- [Insurance](datasets/insurance.json): 678,013 policies. Claims retain shared + policy partitions. The audit records 195 orphan claims and 9,116 positive-count + policies without a payment; 668,897 policies remain for aggregate targets. + Categories stay separate string columns; train-fitted encoding is still pending. + +UCI [Covertype](https://archive.ics.uci.edu/dataset/31/covertype), +[Parkinsons](https://archive.ics.uci.edu/dataset/189/parkinsons+telemonitoring), and +[Concrete](https://archive.ics.uci.edu/dataset/165/concrete+compressive+strength) +pages declare CC BY 4.0. OpenML metadata for +[frequency](https://www.openml.org/api/v1/json/data/41214) and +[severity](https://www.openml.org/api/v1/json/data/41215) declares CC0; downloaded +files also match their published MD5 checksums. Housing now has a matching hosted +CC BY 4.0 declaration in the dated review above. Veteran's original-source license +evidence remains unresolved; StatLib endpoints returned HTTP 403. +Microsoft's linked MSLR download/agreement could not be retrieved; A4 remains +required and unresolved. No missing case is converted into a passing result. + +## Independent evaluation machinery + +- `preprocessing.py` fits numeric medians/missing indicators and categorical + one-hot vocabularies on training rows only; unknown categories use the missing + indicator. Target standardization preserves constant outputs. Training-only + reverse Kaplan-Meier support uses event-before-censor tie handling. +- `freeze_preprocessing.py` freezes encoded arrays for five splits, including + separate claim-level and eligible-policy insurance encoders. Replay with + `build/v1-env/bin/python -m benchmarks.v1.freeze_preprocessing --verify benchmarks/v1/datasets/preprocessing.json`. +- `quality.py` independently recomputes prediction-space primary metrics and + applies the v1-plan-r2 five-fold thresholds. A6 checks every target and A5 every + quantile. Probabilities are validated before the fixed 1e-15 log-loss clipping. +- `quality_report.py` reads hashed NPZ targets/predictions with exact row-ID + alignment. A run directory contains `quality-manifest.json` with declared + paired cells. `python -m benchmarks.v1.quality_report DIRECTORY` reports paired + comparisons; it deliberately does **not** certify E3, because selection receipts + and complete required recipe/device coverage still need integration. +- `process_runner.execute` runs an argv in a fresh directory, captures logs, + enforces wall time and thread settings, kills timed-out process groups, and + requires model/prediction artifacts. Container memory enforcement is separate. + A zero process exit or an execution pass is not a quality pass. +- [Search design](search-design.json) fixes 16 configurations per listed method + family and resource budgets before real quality runs. It remains a partial + design until task adapters, the full matrix, and the agent cohort are bound. + +None of these modules is OpenBoost production training code. Missing ranking +inputs, licenses, held-out tasks, and full runner/judge integration still prevent +F0.3 exit; existing data and metric checks cannot waive those requirements. + +## Installed comparator preflight + +[Capability evidence](evidence/README.md) records the installed CPU and real T4 +matrix, native-build failures and corrected isolated runs. Built-in support is +scoped per task/device. It does not establish real-data quality or OpenBoost GPU +execution. The CUDA environment is hash-locked at the package level; complete +native build provenance remains a final protocol requirement. + +## Numeric validation worker + +`baseline_worker.py JOB.json` writes `predictions.npz` and a trusted local pickle +`model.bin` in its working directory. Jobs name application/library/configuration, +seed, threads, device and input NPZ. Arrays contain training X/y, validation X/IDs, +optional training weights, and task-specific exposure or censoring fields. Unknown +options and test arrays fail. Reloaded predictions must match before files are +written. Use a fresh process through `process_runner.execute`. + +[Worker evidence](evidence/worker-cpu.json) verifies 30 synthetic CPU task/library +fits and in-process reload, including external exposure doubling in all three +count adapters. `worker_smoke.py` reproduces the checks with the locked interpreter. +The original artifact covers fixed-round fitting. Native early stopping is now +supported as described below. Ranking, composed/structural controls and A13 +execution remain required integration work; selection/test release has a separate +audited layer but is not yet bound to the full real-task matrix. + +## Validation selection and sealed test release + +`selection.audit(protocol, records, directory, pinned_protocol_sha256)` independently +recomputes validation metrics for exactly 16 configurations per declared method. +The protocol is held and hashed by the trusted orchestrator before training; it +binds application/fold, code/data/split/preprocessing/environment/search identities, +training row IDs, validation truth, test-feature hash, methods/configurations and +selection weights. Every task primary must have a positive frozen weight. A6 +weights must derive from training target scales; final quality still checks each +output separately. A4 maximizes NDCG; the remaining tasks minimize their scores. +Ties use the lexical trial ID. No test features are opened during selection. + +Trial records contain exactly `id`, `config`, `status`, `exit_code`, +`protocol_sha256`, `prediction`, `model`, and `log`. Artifact descriptors contain +relative `path` and `sha256`; validation predictions retain exact row identity. +The evaluator rejects missing/duplicate/failed trials and producer-supplied scores. +It checks every model/log hash, not only the eventual winner. Receipt contents +include all recomputed metrics, selection scores, artifact descriptors and the +selected trial. Reordering records does not change the receipt. + +`selection.seal` exclusively creates a receipt file and returns its byte hash; +keep that hash separately under orchestrator control. `selection.release_test` +checks this receipt and reruns the complete audit before loading hashed test +features. It rejects training/validation row overlap, test targets, invalid +encoded features and changed artifacts. It returns features and the selected +model descriptor; downstream inference must recheck the model hash before loading. +The audit does not deserialize model files or execute artifact code. + +This is an evaluator access sequence, not an operating-system security boundary. +The protocol/digests must not be chosen by the producer after seeing results. +The audit cannot prove when an external process accessed files or which code it +executed; restricted worker mounts and execution provenance remain necessary. +Query/entity identity constraints belong to frozen dataset adapters, in addition +to this layer's row-disjointness checks. Neither a selection receipt nor the paired +quality report certifies complete E3 coverage. + +Reproduce the actual-process synthetic integration check with the pinned baseline +environment, using a fresh output directory: + +```bash +OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.selection_smoke build/v1-selection-smoke-new +``` + +It executes 16 small fixed-round XGBoost jobs, seals validation selection and runs +selected-model test inference in a new process. The generated packet includes raw +inputs, predictions, models, records and receipt under ignored `build/` output. +The committed [summary](evidence/selection-cpu.json) records source/environment +hashes; it is synthetic harness evidence, not a real-data quality/performance result. + + +## Native baseline early stopping + +Set `early_stopping_rounds` to a positive integer and supply `y_validation`; +`weight_validation` defaults to unit weights. A10 additionally requires +`event_validation`. Without stopping enabled, validation target/weight fields are +rejected rather than silently used or ignored. Targets, weights and survival +indicators are validated before fitting; the test array prohibition is unchanged. + +Native objective metrics determine stopping within each trial. Cross-method +configuration selection still recomputes the frozen primary metrics independently. +The native history preserves its metric names and weighted validation values: + +- XGBoost retains its fitted trees and stores `best_iteration + 1` as an explicit + prediction limit, including vector and quantile models. +- LightGBM records each fitted model's best iteration; per-output/per-quantile + baseline fits stop independently and preserve their individual limits. +- CatBoost uses the validation pool and `use_best_model`, truncating the saved + model to its selected tree count. +- NGBoost receives explicit validation arrays and weights, avoiding an implicit + split. The saved bundle predicts with `best_val_loss_itr + 1`. + +Count validation includes exposure offsets in all three libraries. CLI workers +write `training.json` with stopping histories and prediction limits alongside the +model and predictions. Histories describe native selection, not independent +quality acceptance. The selected limit is part of model replay semantics. + +[CPU stopping evidence](evidence/early-stopping-cpu.json) covers 30 synthetic +supported task/library cells with nonunit validation weights, vector/quantile, +exposure and survival cases. Every selected count matches its history's minimum. +Overfitting counterexamples select round 1 and reproduce in fresh processes. +No GPU stopping or real-data quality result is established. Reproduce with: + +```bash +OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.early_stopping_smoke +OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.selection_smoke build/v1-selection-early-stop-new --early-stopping-rounds 3 +``` + +The second command runs the synthetic 16-trial selection/release smoke with +stopping enabled, using a fresh output directory. Its patience of three is a +small-fixture check; the preregistered real-search patience remains 50. + +## Query-aware ranking worker + +A4 now requires contiguous `query_train` and `query_validation` IDs; partitions +must have disjoint query IDs of the same type. Supply one `query_weight_train` +per contiguous training group and, with stopping, one `query_weight_validation` +per validation group. Defaults are unit query weights. Generic row weights, +fragmented query blocks and invalid relevance labels fail before training. + +The adapters use XGBoost rank:ndcg, LightGBM lambdarank and CatBoost PairLogit. +XGBoost receives per-group weights; LightGBM and CatBoost receive the explicit +native equivalents. Native NDCG@10 histories drive stopping, and prediction +replay preserves the selected model. Independent final scoring uses the fixed +v1 exponential-gain/unit-query NDCG convention; native weighting/gain conventions +are not asserted numerically identical. They remain visible in recorded histories. + +[Ranking evidence](evidence/ranking-cpu.json) covers three CPU fit/stopping/reload +checks. Reproduce with the pinned interpreter and two numerical threads: +`build/v1-env/bin/python -m benchmarks.v1.ranking_smoke`. +The initial smoke assertion selected the last metric in CatBoost's history, +which is PairLogit rather than NDCG; its source/error record is retained. The +corrected check locates NDCG by name. Real MSLR data/agreement and CUDA execution +of this worker remain unverified; these probes do not pass real A4 quality. + +## Parametric and composed validation workers + +`parametric_worker.py JOB.json` runs the `glm` A7/A8/A9, `paid_composition` A9, +or `formula_global` A12 controls through the same fresh-output process runner. +The strict job has `application`, `method`, `config`, and `input_npz`; test arrays +and unsupported fields fail. It writes predictions, a trusted local model bundle +and training configuration after verifying replay. GLMs treat convergence warnings +as failures; nonlinear optimization must report successful finite convergence. + +- GLM A7 consumes period counts plus exposure; A9 consumes period paid totals plus + exposure. Both fit annualized targets with exposure times business weight once. + A7 emits period count predictions; A9 emits annualized premium predictions. + A8 consumes positive individual payments and emits positive payment means. +- Paid composition additionally requires `paid_count`, `claim_policy` indices and + `claim_amount`. Their exact counts and summed positive payments must reconstruct + policy targets. Orphans, nonpositive payments, and mismatches fail. Frequency is + paid-record frequency, not raw ClaimNb. Severity weights inherit each policy's + business weight once per claim. Annualized predictions multiply paid frequency + and severity; period totals multiply exposure once. +- Global formula consumes `age_train`, `age_validation` already in days/28 and + training MPa targets. It fits positive global amplitude/rate through softplus + and records training age support. It is a structural comparator, not FormulaBoost + or a claim of parameter identifiability on arbitrary real datasets. + +All numeric GLM scaling fits training inputs only. [Search design](search-design.json) +now includes 16 paid-composition penalty pairs fixed before real quality runs. +Existing GLM/global-formula grids map directly to worker configuration. These +adapters still need binding to complete real-data/search manifests. + +[Hand-worked evidence](evidence/parametric-cpu.json) verifies weighted means, +paid-record composition, exposure scaling and known-curve parameter recovery. +[CLI evidence](evidence/parametric-worker-cpu.json) verifies all five controls in +bounded processes and exact output units/IDs. Reproduce with the locked CPU +interpreter and two numerical threads: + +```bash +build/v1-env/bin/python -m benchmarks.v1.parametric_smoke +build/v1-env/bin/python -m benchmarks.v1.parametric_worker_smoke build/v1-parametric-workers-new +``` + +These are synthetic correctness checks. Real A9/A12 quality, outer coupled tree +controls, support-stratified reports and full F0.3 integration remain unfinished. + +## Auxiliary quality diagnostics + +`auxiliary.py` adds weighted classification accuracy/Brier/per-class counts, +binary AUC with half credit for score ties, Normal PIT decile mass, survival +IPCW Brier/Harrell C, structural errors by frozen training-age support, and an +exact five-fold empirical bootstrap of paired mean differences. Absent classes, +empty structural strata and no comparable survival pairs produce explicit null +statistics. These diagnostics cannot replace primary gates. + +The [Brier definition](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.metrics.brier_score.html) +uses the training censoring distribution. We use its frozen right-continuous G(t) +with the existing event-before-censor risk convention. Grid points must lie +strictly inside positive training support. For each grid point, only observed +deaths by that point need G at their event times; later observations use G at the +grid point. This avoids extrapolating G for longer test follow-up. It is a direct +formula implementation, not a call to scikit-survival's more restrictive API. +No-contribution grids fail rather than returning a misleading zero. + +Harrell C uses unit comparable pairs, risk = negative log-time location and a +1e-8 risk-tie tolerance. An event tied with a censor is comparable; tied deaths +are not. It is explicitly **not** an IPCW C-index. See the +[concordance definition](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.metrics.concordance_index_censored.html). +Nonunit weights are rejected by this survival auxiliary contract. + +The paired quality report now includes all measured fold metrics and descriptive +paired-difference intervals. Its optional per-cell `auxiliary` entry is: + +- A10: `{"censoring": {"path": "censoring.json", "sha256": "..."}}`, pointing + to the frozen training `censoring_support` object. +- A12: `{"structure": {"path": "structure.npz", "sha256": "..."}}`, with aligned + `row_ids`, `age`, and scalar `train_min`/`train_max` arrays. + +Missing A10/A12 auxiliary inputs are listed in `auxiliary_missing`; invalid or +corrupt inputs produce errors. Primary paired comparisons can be reported while +auxiliaries are missing, but `E3_pass` remains false. Training provenance and full +expected coverage must still be bound independently. Bootstrap intervals enumerate +all 5^5 empirical resamples and are descriptive; overlapping folds are not IID +replications and do not support a population-superiority claim. + +A6 workers accept original-unit matrix targets and fit training-only per-column +mean/std normalization (unweighted, constant-column std one). Native stopping +metrics use standardized targets and zero standardized initialization. Saved +bundles and training receipts retain `target_scale`; exported predictions and +replay are in original units. Final A6 scoring must supply the same training std +for standardized-average RMSE. + +## Frozen real-data worker packets + +`worker_data.py` exports all five Housing (A1/A11), Adult (A2), Covertype (A3), +Bike (A5), Parkinsons (A6), insurance (A7/A8/A9), Veteran (A10), and Concrete +(A12 ordinary GBDT) folds. It checks source arrays, reader hashes, recomputed +training encoders, exact partition hashes, group disjointness and A6 target scale +against the existing freezes. Other applications are explicitly unsupported by +this exporter and remain required work. + +```bash +build/v1-env/bin/python -m benchmarks.v1.worker_data A6 build/a6-packets +build/v1-env/bin/python -m benchmarks.v1.worker_data_smoke build/real-worker-smoke +``` + +Use fresh output directories. Each fold contains a validation worker packet with +explicit early-stopping labels, separate train-row IDs, validation truth, test +features and test truth. The preparer is evaluation-side trusted code; these files +share a directory, so this is not OS-enforced test isolation. The execution runner +must control mounts/access before formal candidate trials. A12 appends age/28 to +ordinary GBDT features and emits separate support artifacts; this packet is not a +FormulaBoost learner input. A6 targets remain in original units; the worker owns +normalization and the frozen scale is retained for independent checking. + +The smoke uses four rounds and patience three, checks row identity and finite +output shapes, and verifies the saved A6 scale. The worker checks reload before +emitting artifacts. It does not read test truth, select a model, or certify E3. + +Adult packets retain official source/physical-line IDs; its official test set +is unchanged across the five stratified training splits. Categorical vocabularies +are fitted on training rows and checked against the freeze. Covertype retains +all seven classes and source row positions. Bike preserves `instant` source IDs, +expanding chronological windows and date-disjoint boundaries. Later observations +are excluded from each earlier origin; they are not forced into that fold's test +set. Preprocessing hashes bind positional source indices, while prediction/truth +packets carry the original row IDs where provided. + +The exporter validates all folds before writing packets, then materializes one +fold at a time to limit memory use on Covertype. It still emits dense controls; +this is not a memory or throughput claim for the future foundation. + +The exporter also binds policy counts (A7), positive individual paid claims (A8), +eligible-policy annualized paid totals (A9), and event/right-censored AFT inputs +(A10). Insurance applications inherit the same policy partitions; claims and +eligible policies use their separately frozen training encoders. A7 preserves +raw integer counts and supplies exposure for the worker's offset. A8 uses unit +weights per paid claim; its row ID is the retained joined claim position, while +policy ID controls grouping. A9 uses paid total/exposure and exposure weights, +with no exposure offset; separate period artifacts retain totals and exposure. +These numeric worker packets are not inputs for the parametric composition worker. + +A10 restores the source-declared categorical features, exports event indicators +and hashes a `censoring.json` containing the frozen training reverse-KM estimate +and supported grid. Veteran original-source license review remains unresolved; +local adapter checks do not resolve that source gate or certify survival quality. + +```bash +build/v1-env/bin/python -m benchmarks.v1.worker_data_smoke build/positive-survival-smoke --applications A7 A8 A9 A10 +``` + +The smoke accepts an explicit subset of supported applications and records it in +its command. Without that option it checks every supported application. A4 and +A13 remain unsupported here and required in the full evaluation plan. + +## Current OpenBoost worker (A1/A11 integration wave) + +`openboost_worker.py JOB.json` consumes the same encoded train/validation packet +keys emitted by `worker_data.export`. It currently accepts A1 squared and A11 +joint natural/ordinary Normal only, library=openboost, device=cpu, threads=1. +Run through `process_runner.execute(..., threads=1)` to set process thread limits. +Both tasks require explicit validation labels, even without patience. Unknown +options, unsupported tasks and test arrays fail rather than being ignored. +Other required application adapters remain pending. + +A fixed budget exports the final model. Enabled patience exports the strict best +validation snapshot, including the initial base if no step improves it. Training +metadata records outer rounds, accepted commits, stop reason and selected model +identity; these counts are not interchangeable. A1 selection uses half squared +loss (same ordering as MSE); A11 uses Normal NLL. Internal training loss governs +Normal backtracking separately from validation selection. + +`model.bin` is a versioned **JSON** evaluation bundle, not a pickle: a core raw +model plus application/output semantics. A1 returns mean `[N]`; A11 returns +mean and standard deviation `[N,2]` in the supplied target units. No target +scaling or offsets are added by these encoded-data adapters. The separate +`openboost_predict.py MODEL FEATURES OUTPUT` loads a packet with only `x` and +`row_ids` and does not import training recipes. It rejects mismatched output +semantics. The benchmark bundle is not a new stable public persistence API. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-current-worker-044 +``` + +This runs four-round A1/A11 trials on each of the five frozen housing folds and +checks exact validation replay in a new process. It reads no test truth during +training or scoring, performs no configuration search and makes no quality or +performance claim. Exported packet separation is not an OS access boundary. +Formal test-label isolation, all application bindings and E3 remain open. + +### A6 multi-output integration + +The current worker also accepts A6 finite matrix targets and `mode=shared` or +`mode=independent`. It computes the same **unweighted training-population** mean +and standard deviation as the frozen evaluator/comparator protocol, even when +training weights are supplied. This is intentionally different from the general +public `TargetScale.fit`, which uses weights. The worker constructs a public +TargetScale from the frozen convention and uses it for both training and validation. +Sample weights still apply to loss/derivatives. Constant target scales use one. + +Best-model selection and patience use the row-weighted mean of the sum of +standardized half squared errors across outputs. For a fixed output width this +has the same ordering as their mean; the recorded score retains the sum convention. +The saved A6 bundle includes `target_scale` (mean/std/constant), and restored +predictions apply the inverse transform once, returning original target units. +Training metadata records the convention and scale. This does not implement +cross-configuration A13 selection or the full A6 quality protocol. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-multi-worker-045 --applications A6 +``` + +The smoke verifies all five grouped Parkinsons folds and exact scale equality +with the freeze. Default smoke applications are now A1/A6/A11; explicit application +selection permits bounded reruns. Four-round results are integration evidence only. + +### Scale-bound A6 selection and current search + +A6 selection protocols now require `train_targets` (hashed NPZ with row_ids/y) +and `target_scale` (hashed JSON with mean/std/constant). The independent audit +aligns targets exactly to training row IDs, recomputes the unweighted population +scale and rejects differences. Every `rmse_k` selection weight must equal the +inverse frozen standard deviation. The reported selection score is the mean +of these standardized RMSEs, not division by the sum of inverse scales. +Other applications retain their existing score definitions. + +Protocol digests must still be pinned by the trusted orchestrator. This checks +internal consistency with supplied training data, not external dataset provenance +or filesystem isolation. All 16 configurations per method must finish successfully; +missing/failed/tampered trials cannot yield a receipt or selected test release. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.current_selection_smoke /tmp/openboost-selection-046 +``` + +This synthetic A6/A13 integration runs 16 current configurations (shared and +independent trees, two learning rates and four depths), audits validation scores, +seals/re-audits the winner, and invokes fresh-process inference only after feature +release. All trials and failures are retained. It scores no test labels and is +not the frozen real-data quality grid, a speed benchmark or fused train-many. +A6 final comparative quality reporting and the remaining real searches remain open. + +### A6 paired quality reporting + +A6 quality cells require all `rmse_k` primary metrics followed by +`standardized_rmse`. Their `auxiliary` object must supply hashed `train_rows` +(NPZ row_ids), `train_targets` (NPZ row_ids/y), and `target_scale` (JSON +mean/std/constant). The report verifies aligned, unique training IDs, disjointness +from evaluation rows, matching target width and an exactly recomputed unweighted +training-population scale. Missing or inconsistent support is an error. + +The standardized metric is the arithmetic mean of per-target original-unit RMSE +divided by each verified training standard deviation. Constant targets use scale +one. It is reported for each fold and compared across five paired folds alongside +every target. A passing average cannot override a failed target. As before, this +layer never marks E3 complete: selected-model provenance, full required coverage +and trusted source/protocol identity remain separate obligations. + +### A2/A3 current classification workers + +Current classification jobs require `classes=2` for A2 or an integer count of at +least three for A3. Targets are integer codes in `[0, classes)`; every declared +class must occur in training. Class order is persisted as `0,1,...,K-1`. The +prediction loader rejects missing or reordered class schemas. A2 emits P(class=1) +as `[N]`; A3 emits `[N,K]` probabilities in canonical encoded order. Patience/best +selection uses weighted log loss, and fixed-budget selection uses the final model. + +External validation IDs may be unique integers or strings. Workers validate and +preserve them in emitted artifacts, while public data records use local integer +indices. These indices are execution-local and never replace exported source IDs. +Unsupported fields and label/class mismatches fail explicitly. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-classification-048-fixed --applications A2 +``` + +A2 has five-fold frozen Adult validation integration evidence. A3 has weighted +synthetic direct-recipe/fresh-process checks in this slice; full Covertype runs +remain pending. The smoke accepts explicit A2/A3 selections, with seven classes +for Covertype. Defaults remain A1/A6/A11 to avoid silently broadening existing +runs. These four-round probes establish no classification quality/calibration, +search, CUDA or speed claim. diff --git a/benchmarks/v1/__init__.py b/benchmarks/v1/__init__.py new file mode 100644 index 0000000..b587e7d --- /dev/null +++ b/benchmarks/v1/__init__.py @@ -0,0 +1 @@ +"""Versioned v1 evaluation tooling; independent of production training.""" diff --git a/benchmarks/v1/adapter_smoke.py b/benchmarks/v1/adapter_smoke.py new file mode 100644 index 0000000..e679211 --- /dev/null +++ b/benchmarks/v1/adapter_smoke.py @@ -0,0 +1,144 @@ +"""Probe baseline category/weight and explicit exposure prediction semantics.""" + +import hashlib +import importlib.metadata +import json +import pickle +import platform +import subprocess +from pathlib import Path + +import numpy as np + + +def run(): + import catboost as cb + import lightgbm as lgb + import pandas as pd + import xgboost as xgb + from sklearn.linear_model import PoissonRegressor + + rng = np.random.default_rng(7) + n = 96 + x = rng.normal(size=(n, 3)) + x[::9, 0] = np.nan + category = np.array(["a", "b", "missing"] * (n // 3)) + frame = pd.DataFrame(x, columns=["x0", "x1", "x2"]) + frame["category"] = pd.Categorical(category, categories=["a", "b", "missing"]) + y = (np.nan_to_num(x[:, 0]) + rng.normal(size=n) > 0.1).astype(int) + w = np.where(y, 3.0, 0.5) + result = [] + for lib in ["xgboost", "lightgbm", "catboost"]: + predictions = [] + for weight in [w, np.ones(n)]: + if lib == "xgboost": + d = xgb.DMatrix(frame, label=y, weight=weight, enable_categorical=True) + m = xgb.train( + { + "objective": "binary:logistic", + "tree_method": "hist", + "max_depth": 2, + "nthread": 2, + "seed": 7, + }, + d, + num_boost_round=4, + ) + p = m.predict(d) + elif lib == "lightgbm": + d = lgb.Dataset(frame, label=y, weight=weight, categorical_feature=["category"]) + m = lgb.train( + { + "objective": "binary", + "num_leaves": 4, + "num_threads": 2, + "min_data_in_leaf": 2, + "verbosity": -1, + "seed": 7, + }, + d, + num_boost_round=4, + ) + p = m.predict(frame) + else: + d = cb.Pool(frame, label=y, weight=weight, cat_features=["category"]) + m = cb.CatBoostClassifier( + iterations=4, + depth=2, + thread_count=2, + verbose=False, + random_seed=7, + allow_writing_files=False, + ).fit(d) + p = m.predict_proba(d)[:, 1] + if not np.isfinite(p).all() or not np.all((p >= 0) & (p <= 1)): + raise ValueError("invalid class probabilities") + predictions.append(p) + difference = float(np.max(np.abs(predictions[0] - predictions[1]))) + if difference == 0: + raise ValueError("weights had no observable effect") + result.append( + { + "case": lib + " categories/missing/nonunit weights", + "status": "pass", + "weight_prediction_difference": difference, + } + ) + # A declared rate model carries exposure outside the saved model at inference. + clean = np.nan_to_num(x) + exposure = np.linspace(0.1, 2, n) + count = rng.poisson(exposure * np.exp(clean[:, 0] / 3)) + m = PoissonRegressor(alpha=0.1, max_iter=1000).fit( + clean, count / exposure, sample_weight=w * exposure + ) + loaded = pickle.loads(pickle.dumps(m)) + rate = loaded.predict(clean) + np.testing.assert_array_equal(m.predict(clean) * exposure, rate * exposure) + np.testing.assert_array_equal(rate * (2 * exposure), 2 * (rate * exposure)) + result.append({"case": "saved Poisson rate model plus explicit exposure", "status": "pass"}) + # XGBoost base_margin is supplied again after loading; it is not model state. + d = xgb.DMatrix(clean, label=count, weight=w, base_margin=np.log(exposure)) + m = xgb.train( + { + "objective": "count:poisson", + "tree_method": "hist", + "max_depth": 2, + "nthread": 2, + "seed": 7, + }, + d, + num_boost_round=4, + ) + loaded = xgb.Booster(model_file=m.save_raw()) + p1 = loaded.predict(xgb.DMatrix(clean, base_margin=np.log(exposure))) + p2 = loaded.predict(xgb.DMatrix(clean, base_margin=np.log(2 * exposure))) + np.testing.assert_allclose(p2, 2 * p1, rtol=1e-6, atol=1e-7) + result.append( + { + "case": "XGBoost supplied base_margin after reload", + "status": "pass", + "double_exposure_max_error": float(np.max(np.abs(p2 - 2 * p1))), + } + ) + return { + "scope": "CPU adapter probes only; full real-task pipeline pending", + "cells": result, + "environment": { + "python": platform.python_version(), + "os": platform.platform(), + "packages": {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + }, + } + + +if __name__ == "__main__": + result = run() + result.update( + source_sha=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + source_file_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + ) + Path("benchmarks/v1/evidence/adapter-cpu.json").write_text( + json.dumps(result, indent=2, sort_keys=True) + "\n" + ) + print(result["cells"]) diff --git a/benchmarks/v1/adult.py b/benchmarks/v1/adult.py new file mode 100644 index 0000000..7b9bd6e --- /dev/null +++ b/benchmarks/v1/adult.py @@ -0,0 +1,216 @@ +"""Pinned Adult data and official-test-preserving stratified evaluation splits.""" + +import argparse +import csv +import hashlib +import io +import json +import platform +import subprocess +import sys +import zipfile +from pathlib import Path + +import numpy as np + +SOURCE = "https://archive.ics.uci.edu/static/public/2/adult.zip" +ARCHIVE_SHA256 = "7537312dd56c2b98035880805ce99e68183a30ee468aa5329d6df0fbb3cc21bb" +MEMBERS = { + "adult.data": "5b00264637dbfec36bdeaab5676b0b309ff9eb788d63554ca0a249491c86603d", + "adult.test": "a2a9044bc167a35b2361efbabec64e89d69ce82d9790d2980119aac5fd7e9c05", +} +COLUMNS = ( + "age", + "workclass", + "fnlwgt", + "education", + "education-num", + "marital-status", + "occupation", + "relationship", + "race", + "sex", + "capital-gain", + "capital-loss", + "hours-per-week", + "native-country", +) +NUMERIC = {"age", "education-num", "capital-gain", "capital-loss", "hours-per-week"} +FEATURES = tuple(c for c in COLUMNS if c != "fnlwgt") + + +def digest(data): + return hashlib.sha256(data).hexdigest() + + +def canonical(value): + return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode() + + +def parse_source(content, source): + if source not in MEMBERS: + raise ValueError("unknown official source") + result = {"x": [], "y": [], "row_ids": []} + for line_number, line in enumerate(content.decode("utf-8").splitlines(), 1): + if not line.strip(): + continue + if source == "adult.test" and line_number == 1 and line == "|1x3 Cross validator": + continue + values = [v.strip() for v in next(csv.reader([line]))] + if len(values) != 15 or any(not v for v in values): + raise ValueError("invalid Adult row schema") + label = values[-1] + if source == "adult.test": + if not label.endswith("."): + raise ValueError("official test label needs one trailing dot") + label = label[:-1] + if label not in ("<=50K", ">50K"): + raise ValueError("invalid Adult label") + row = [] + for name, value in zip(COLUMNS, values[:-1], strict=True): + if name == "fnlwgt": + continue # never infer sample weight from a source column + if value == "?": + if name in NUMERIC: + raise ValueError("numeric missing value outside source contract") + row.append(None) + elif name in NUMERIC: + number = int(value) + if number < 0: + raise ValueError("negative numeric feature") + row.append(number) + else: + row.append(value) + result["x"].append(row) + result["y"].append(int(label == ">50K")) + result["row_ids"].append(f"{source}:{line_number}") + if not result["y"]: + raise ValueError("empty Adult source") + return result + + +def load_archive(path): + content = Path(path).read_bytes() + if digest(content) != ARCHIVE_SHA256: + raise ValueError("archive hash mismatch") + result = {} + with zipfile.ZipFile(io.BytesIO(content)) as archive: + for member, expected in MEMBERS.items(): + data = archive.read(member) + if digest(data) != expected: + raise ValueError("member hash mismatch") + result[member] = parse_source(data, member) + return result["adult.data"], result["adult.test"] + + +def stratified_split(labels, seed): + labels = np.asarray(labels) + if ( + labels.ndim != 1 + or labels.dtype.kind not in "iu" + or set(labels.tolist()) != {0, 1} + or type(seed) is not int + or seed < 0 + ): + raise ValueError("binary integer labels and nonnegative integer seed required") + rng = np.random.default_rng(seed) + train, validation = [], [] + for label in (0, 1): + rows = rng.permutation(np.flatnonzero(labels == label)) + end = len(rows) * 4 // 5 + if not 0 < end < len(rows): + raise ValueError("each class needs train and validation rows") + train.extend(rows[:end]) + validation.extend(rows[end:]) + return np.sort(np.array(train, dtype="50K"], + "weight": "unit; fnlwgt excluded", + "hash_format": "SHA256(canonical JSON with sorted keys and compact separators; ensure_ascii=True)", + "source_records": { + name: { + "rows": len(data["y"]), + "data_sha256": digest(canonical(data)), + "missing_by_feature": [ + sum(row[j] is None for row in data["x"]) for j in range(len(FEATURES)) + ], + } + for name, data in (("adult.data", train), ("adult.test", test)) + }, + "test_rows_with_predictors_seen_in_official_train": sum( + canonical(row) in train_x for row in test["x"] + ), + "identity_note": "source:physical-line IDs; repeated predictor rows retained; no person IDs available", + "split_rule": "official test unchanged; per seed PCG64 default_rng, class 0 then 1 permutation; floor(0.8*n_class) train, rest validation; sort source indices", + "folds": folds, + "status": "raw data/splits only; numeric encoding, budgets and quality pending", + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("archive", type=Path) + parser.add_argument("--verify", type=Path) + args = parser.parse_args() + record = describe(args.archive) + if args.verify: + frozen = json.loads(args.verify.read_text()) + provenance = frozen.pop("provenance") + if frozen != record or provenance["adapter_sha256"] != digest(Path(__file__).read_bytes()): + raise ValueError("frozen data/splits/source mismatch") + root = Path(__file__).resolve().parents[2] + record["provenance"] = { + "adapter_sha256": digest(Path(__file__).read_bytes()), + "git_sha": subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=root, text=True + ).strip(), + "dirty": bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=root)), + "python": platform.python_version(), + "numpy": np.__version__, + "os": platform.platform(), + "argv": ["python", "-m", "benchmarks.v1.adult", *sys.argv[1:]], + } + print(json.dumps(record, indent=2, sort_keys=True, allow_nan=False)) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/auxiliary.py b/benchmarks/v1/auxiliary.py new file mode 100644 index 0000000..a5fcff3 --- /dev/null +++ b/benchmarks/v1/auxiliary.py @@ -0,0 +1,205 @@ +"""Independent auxiliary metrics; diagnostics never replace primary task gates.""" + +import math + +import numpy as np + +from benchmarks.v1.quality import metrics + + +def _weights(rows, weight): + w = np.ones(rows) if weight is None else np.asarray(weight, dtype=float) + if w.shape != (rows,) or not np.isfinite(w).all() or np.any(w < 0) or w.sum() <= 0: + raise ValueError("invalid evaluation weights") + return w + + +def classification(application, target, prediction, weight=None): + primary = metrics(application, target, prediction, weight=weight) + if application not in ["A2", "A3"]: + raise ValueError("classification application required") + y, p = np.asarray(target), np.asarray(prediction) + if y.ndim != 1: + raise ValueError("scalar class IDs required") + if application == "A2": + p = np.column_stack([1 - p, p]) + w = _weights(len(y), weight) + selected = np.argmax(p, axis=1) + one_hot = np.eye(p.shape[1])[y.astype(int)] + brier = (p[:, 1] - y) ** 2 if application == "A2" else ((p - one_hot) ** 2).sum(axis=1) + classes = {} + for k in range(p.shape[1]): + positive, chosen = y == k, selected == k + mass, predicted_mass = w[positive].sum(), w[chosen].sum() + true_positive = w[positive & chosen].sum() + classes[str(k)] = dict( + rows=int(positive.sum()), + weight=float(mass), + recall=float(true_positive / mass) if mass else None, + precision=float(true_positive / predicted_mass) if predicted_mass else None, + ) + auc = None + if application == "A2": + pos_mass, neg_mass = w[y == 1].sum(), w[y == 0].sum() + if pos_mass and neg_mass: + # Ascending score groups give half credit to positive/negative ties. + order = np.argsort(p[:, 1], kind="stable") + score, yy, ww = p[order, 1], y[order], w[order] + starts = np.r_[0, np.flatnonzero(score[1:] != score[:-1]) + 1, len(y)] + below, numerator = 0.0, 0.0 + for a, b in zip(starts[:-1], starts[1:], strict=True): + positive = ww[a:b][yy[a:b] == 1].sum() + negative = ww[a:b][yy[a:b] == 0].sum() + numerator += positive * (below + 0.5 * negative) + below += negative + auc = float(numerator / (pos_mass * neg_mass)) + return dict( + **primary, + accuracy=float(np.average(selected == y, weights=w)), + brier=float(np.average(brier, weights=w)), + binary_auc=auc, + classes=classes, + ) + + +def normal_pit(target, prediction, weight=None): + result = metrics("A11", target, prediction, weight=weight) + y, p = np.asarray(target), np.asarray(prediction) + z = (y - p[:, 0]) / p[:, 1] + pit = np.array([0.5 * math.erfc(-v / math.sqrt(2)) for v in z]) + w = _weights(len(y), weight) + result["pit_decile_mass"] = ( + np.histogram(pit, bins=np.linspace(0, 1, 11), weights=w)[0] / w.sum() + ).tolist() + return result + + +def survival(time, event, prediction, support, weight=None): + """Log-normal IPCW Brier on a frozen training censoring grid, plus Harrell C. + + G is evaluated as a right-continuous step, matching the documented Brier + convention. No G values beyond the frozen support are extrapolated. Harrell + C uses unit pairs and is not an IPCW estimate; weighted C is rejected. + """ + t, e, p = np.asarray(time, dtype=float), np.asarray(event), np.asarray(prediction, dtype=float) + result = metrics("A10", t, p, event=e, weight=weight) + w = _weights(len(t), weight) + if not np.all(w == 1): + raise ValueError("survival auxiliary contract currently requires unit row weights") + if set(support) != {"times", "survival", "grid", "strict_upper", "tie_rule"}: + raise ValueError("invalid censoring support fields") + times = np.asarray(support["times"], dtype=float) + g = np.asarray(support["survival"], dtype=float) + grid = np.asarray(support["grid"], dtype=float) + upper = support["strict_upper"] + if ( + times.ndim != 1 + or not len(times) + or g.shape != times.shape + or not np.isfinite(times).all() + or np.any(times <= 0) + or np.any(np.diff(times) <= 0) + or not np.isfinite(g).all() + or np.any((g < 0) | (g > 1)) + or np.any(np.diff(g) > 0) + ): + raise ValueError("invalid training censoring curve") + expected_upper = times[np.flatnonzero(g == 0)[0]] if np.any(g == 0) else times[-1] + if upper != expected_upper or support.get("tie_rule") != "events removed before censoring risk": + raise ValueError("censoring support/tie contract differs") + if ( + grid.ndim != 1 + or not len(grid) + or not np.isfinite(grid).all() + or np.any(grid <= 0) + or np.any(np.diff(grid) <= 0) + or np.any(grid >= upper) + ): + raise ValueError("grid outside training censoring support") + + def G(value): + index = np.searchsorted(times, value, side="right") - 1 + return np.where(index < 0, 1.0, g[np.maximum(index, 0)]) + + scores = [] + contributing = [] + for point in grid: + z = (math.log(point) - p[:, 0]) / p[:, 1] + s = np.array([0.5 * math.erfc(v / math.sqrt(2)) for v in z]) + deaths = (t <= point) & (e == 1) + alive = t > point + denominator = G(t[deaths]) + gt = float(G(point)) + if np.any(denominator <= 0) or gt <= 0 or not np.any(deaths | alive): + raise ValueError("no supported IPCW contributions") + values = np.zeros(len(t)) + values[deaths] = s[deaths] ** 2 / denominator + values[alive] = (1 - s[alive]) ** 2 / gt + scores.append(float(values.mean())) + contributing.append(int(np.sum(deaths | alive))) + # Unit comparable pairs: event precedes later observation, including an event + # tied with a censor. Two tied deaths do not form an ordered pair. + risk = -p[:, 0] + concordant = 0.0 + comparable = 0 + tied = 0 + for i in np.flatnonzero(e): + js = np.flatnonzero((t > t[i]) | ((t == t[i]) & (e == 0))) + delta = risk[i] - risk[js] + ties = np.abs(delta) <= 1e-8 + concordant += float(np.sum(delta > 1e-8)) + 0.5 * float(ties.sum()) + comparable += len(js) + tied += int(ties.sum()) + result.update( + grid=grid.tolist(), + ipcw_brier=scores, + contributing_rows=contributing, + integrated_brier=float(np.trapezoid(scores, grid) / (grid[-1] - grid[0])) + if len(grid) > 1 + else None, + harrell_c=concordant / comparable if comparable else None, + comparable_pairs=comparable, + tied_risk_pairs=tied, + ) + return result + + +def structure_errors(age, target, prediction, train_min, train_max, weight=None): + metrics("A12", target, prediction, weight=weight) + age, y, p = np.asarray(age, dtype=float), np.asarray(target), np.asarray(prediction) + if ( + age.shape != y.shape + or not np.isfinite(age).all() + or np.any(age <= 0) + or not np.isfinite([train_min, train_max]).all() + or not 0 < train_min <= train_max + ): + raise ValueError("invalid structural support") + w = _weights(len(y), weight) + result = {} + for name, mask in dict( + below=age < train_min, inside=(age >= train_min) & (age <= train_max), above=age > train_max + ).items(): + result[name] = dict( + rows=int(mask.sum()), + rmse=float(np.sqrt(np.average((y[mask] - p[mask]) ** 2, weights=w[mask]))) + if w[mask].sum() > 0 + else None, + ) + return result + + +def paired_interval(candidate, baseline): + """Exact five-fold empirical bootstrap of mean paired differences, descriptive only.""" + c, b = np.asarray(candidate, dtype=float), np.asarray(baseline, dtype=float) + if c.shape != (5,) or b.shape != (5,) or not np.isfinite([c, b]).all(): + raise ValueError("five finite paired folds required") + delta = c - b + indices = np.indices((5,) * 5).reshape(5, -1) + means = delta[indices].mean(axis=0) + return dict( + differences=delta.tolist(), + mean=float(delta.mean()), + percentile95=np.quantile(means, [0.025, 0.975]).tolist(), + interpretation="descriptive empirical fold bootstrap; overlapping splits are not independent evidence", + ) diff --git a/benchmarks/v1/baseline_worker.py b/benchmarks/v1/baseline_worker.py new file mode 100644 index 0000000..e04f743 --- /dev/null +++ b/benchmarks/v1/baseline_worker.py @@ -0,0 +1,532 @@ +"""One numeric baseline trial producing validation predictions and a saved model. + +Input NPZ fields: x_train, y_train, x_validation, validation_row_ids; optional +weight_train, exposure_train/exposure_validation, event_train. Early stopping +requires y_validation and accepts weight_validation/event_validation. Preprocessing and +split identity are supplied by the frozen caller. No test arrays are read. +""" + +import argparse +import json +import math +import pickle +from pathlib import Path + +import numpy as np + + +def fit(job, arrays): + task, library = job["application"], job["library"] + if task not in {f"A{i}" for i in range(1, 13)}: + raise ValueError("unsupported worker task") + if library not in ["xgboost", "lightgbm", "catboost", "ngboost"]: + raise ValueError("unknown baseline library") + allowed_job = { + "application", + "library", + "seed", + "threads", + "device", + "config", + "classes", + "early_stopping_rounds", + "input_npz", + } + if set(job) - allowed_job: + raise ValueError("unsupported job fields") + allowed_arrays = {"x_train", "y_train", "x_validation", "validation_row_ids", "weight_train"} + patience = job.get("early_stopping_rounds") + if patience is not None: + if type(patience) is not int or patience <= 0: + raise ValueError("positive integer early stopping patience required") + allowed_arrays.update({"y_validation", "weight_validation"}) + if task == "A10": + allowed_arrays.add("event_validation") + if task == "A4": + allowed_arrays.update({"query_train", "query_validation", "query_weight_train"}) + if patience is not None: + allowed_arrays.add("query_weight_validation") + if task == "A7": + allowed_arrays.update({"exposure_train", "exposure_validation"}) + if task == "A10": + allowed_arrays.add("event_train") + if set(arrays) - allowed_arrays: + raise ValueError("unsupported input arrays") + x, y = arrays["x_train"], arrays["y_train"] + v = arrays["x_validation"] + if x.ndim != 2 or v.ndim != 2 or x.shape[1] != v.shape[1] or len(y) != len(x): + raise ValueError("invalid training schema") + if not np.isfinite(x).all() or not np.isfinite(v).all() or not np.isfinite(y).all(): + raise ValueError("encoded finite inputs required") + if job["device"] not in {"cpu", "cuda"}: + raise ValueError("unsupported device") + if type(job["threads"]) is not int or job["threads"] <= 0: + raise ValueError("positive thread count required") + if type(job["seed"]) is not int or job["seed"] < 0: + raise ValueError("nonnegative integer seed required") + ids = arrays["validation_row_ids"] + if ids.ndim != 1 or len(ids) != len(v) or len(np.unique(ids)) != len(ids): + raise ValueError("invalid validation row IDs") + if task == "A10": + event = arrays["event_train"] + if event.shape != y.shape or not np.isin(event, [0, 1]).all() or np.any(y <= 0): + raise ValueError("invalid survival targets") + ranking = None + if task == "A4": + # This file is also invoked directly by process_runner. + if __package__: + from benchmarks.v1.ranking import validate + else: + from ranking import validate + ranking = validate(arrays, patience) + w = arrays.get("weight_train", np.ones(len(y))) + if w.shape != (len(y),) or np.any(w < 0) or not np.isfinite(w).all() or w.sum() <= 0: + raise ValueError("invalid sample weights") + cfg = dict(job["config"]) + rounds = cfg.pop("rounds") + seed = job["seed"] + lr = cfg.pop("learning_rate") + if cfg.pop("seed_from_fold", True) is not True: + raise ValueError("seed semantics differ") + vy = vw = None + if patience is not None: + if "y_validation" not in arrays: + raise ValueError("early stopping requires explicit validation targets") + vy = arrays["y_validation"] + vw = arrays.get("weight_validation", np.ones(len(v))) + if vy.shape != (len(v), *y.shape[1:]) or not np.isfinite(vy).all(): + raise ValueError("invalid validation targets") + if vw.shape != (len(v),) or not np.isfinite(vw).all() or np.any(vw < 0) or vw.sum() <= 0: + raise ValueError("invalid validation weights") + if task == "A10": + ve = arrays.get("event_validation") + if ( + ve is None + or ve.shape != vy.shape + or not np.isin(ve, [0, 1]).all() + or np.any(vy <= 0) + ): + raise ValueError("invalid validation survival targets") + target_scale = None + if task == "A6": + if y.ndim != 2 or not y.shape[1]: + raise ValueError("nonempty matrix targets required for A6") + if __package__: + from benchmarks.v1.preprocessing import fit_target_scale + else: + from preprocessing import fit_target_scale + target_scale = fit_target_scale(y) + mean, std = np.asarray(target_scale["mean"]), np.asarray(target_scale["std"]) + y = (y - mean) / std + if vy is not None: + vy = (vy - mean) / std + stopping = [] + prediction_rounds = None + if type(rounds) is not int or rounds <= 0: + raise ValueError("positive rounds required") + exposure = validation_exposure = None + base = 0.0 + if task == "A7": + exposure = arrays["exposure_train"] + validation_exposure = arrays["exposure_validation"] + if ( + exposure.shape != (len(y),) + or validation_exposure.shape != (len(v),) + or not np.isfinite(exposure).all() + or not np.isfinite(validation_exposure).all() + or np.any(exposure <= 0) + or np.any(validation_exposure <= 0) + ): + raise ValueError("invalid exposure") + base = math.log(max(float(np.dot(w, y) / np.dot(w, exposure)), 1e-12)) + prediction = None + if library == "xgboost": + import xgboost as xgb + + if task == "A11": + raise ValueError("Normal needs NGBoost/CatBoost or an outer-loop adapter") + if set(cfg) != {"max_depth", "reg_lambda"}: + raise ValueError("unsupported XGBoost parameters") + objectives = { + "A1": "reg:squarederror", + "A2": "binary:logistic", + "A3": "multi:softprob", + "A4": "rank:ndcg", + "A5": "reg:quantileerror", + "A6": "reg:squarederror", + "A7": "count:poisson", + "A8": "reg:gamma", + "A9": "reg:tweedie", + "A10": "survival:aft", + "A12": "reg:squarederror", + } + params = dict( + cfg, + objective=objectives[task], + eta=lr, + nthread=job["threads"], + seed=seed, + device=job["device"], + tree_method="hist", + ) + if task == "A3": + params["num_class"] = job["classes"] + if task == "A6": + params["multi_strategy"] = "multi_output_tree" + params["base_score"] = 0.0 + if task == "A5": + params["quantile_alpha"] = [0.1, 0.5, 0.9] + if task == "A9": + params["tweedie_variance_power"] = 1.5 + d = xgb.DMatrix(x, label=None if task == "A10" else y, weight=None if task == "A4" else w) + validation = xgb.DMatrix(v) + if task == "A4": + params["eval_metric"] = "ndcg@10" + d.set_group(ranking[0][1]) + d.set_weight(ranking[0][2]) + validation.set_group(ranking[1][1]) + if task == "A7": + d.set_base_margin(base + np.log(exposure)) + validation.set_base_margin(base + np.log(validation_exposure)) + if task == "A10": + event = arrays["event_train"] + d.set_float_info("label_lower_bound", y) + d.set_float_info("label_upper_bound", np.where(event, y, np.inf)) + params.update(aft_loss_distribution="normal", aft_loss_distribution_scale=1.0) + history = {} + if patience is not None: + if task == "A10": + validation.set_float_info("label_lower_bound", vy) + validation.set_float_info( + "label_upper_bound", np.where(arrays["event_validation"], vy, np.inf) + ) + else: + validation.set_label(vy) + validation.set_weight(ranking[1][2] if task == "A4" else vw) + model = xgb.train( + params, + d, + num_boost_round=rounds, + evals=[(validation, "validation")] if patience is not None else [], + early_stopping_rounds=patience, + evals_result=history, + verbose_eval=False, + ) + if patience is not None: + prediction_rounds = model.best_iteration + 1 + stopping.append(dict(selected_rounds=prediction_rounds, history=history)) + actual = json.loads(model.save_config())["learner"]["generic_param"]["device"] + if job["device"] == "cuda" and not actual.startswith("cuda"): + raise ValueError("silent CPU fallback") + prediction = model.predict( + validation, output_margin=task == "A10", iteration_range=(0, prediction_rounds or 0) + ) + if task == "A10": + prediction = np.column_stack([prediction, np.ones(len(v))]) + elif library == "lightgbm": + import lightgbm as lgb + + if task in ["A10", "A11"]: + raise ValueError("no builtin matching task") + if set(cfg) != {"num_leaves", "lambda_l2"}: + raise ValueError("unsupported LightGBM parameters") + objectives = { + "A1": "regression", + "A2": "binary", + "A3": "multiclass", + "A4": "lambdarank", + "A5": "quantile", + "A6": "regression", + "A7": "poisson", + "A8": "gamma", + "A9": "tweedie", + "A12": "regression", + } + params = dict( + cfg, + objective=objectives[task], + learning_rate=lr, + num_threads=job["threads"], + seed=seed, + device_type=job["device"], + verbosity=-1, + ) + if task == "A3": + params["num_class"] = job["classes"] + if task == "A9": + params["tweedie_variance_power"] = 1.5 + if task == "A4": + params.update(metric="ndcg", eval_at=[10]) + if task == "A6": + params["boost_from_average"] = False + targets = y.T if task == "A6" else [y] * 3 if task == "A5" else [y] + model = [] + predictions = [] + for k, target in enumerate(targets): + if task == "A5": + params["alpha"] = [0.1, 0.5, 0.9][k] + d = lgb.Dataset( + x, + label=target, + weight=np.repeat(ranking[0][2], ranking[0][1]) if task == "A4" else w, + group=ranking[0][1] if task == "A4" else None, + init_score=base + np.log(exposure) if task == "A7" else None, + ) + history = {} + valid = [] + callbacks = [] + if patience is not None: + vt = vy[:, k] if task == "A6" else vy + valid = [ + lgb.Dataset( + v, + label=vt, + weight=np.repeat(ranking[1][2], ranking[1][1]) if task == "A4" else vw, + group=ranking[1][1] if task == "A4" else None, + reference=d, + init_score=base + np.log(validation_exposure) if task == "A7" else None, + ) + ] + callbacks = [ + lgb.early_stopping(patience, verbose=False), + lgb.record_evaluation(history), + ] + m = lgb.train( + params, + d, + num_boost_round=rounds, + valid_sets=valid, + valid_names=["validation"] if valid else None, + callbacks=callbacks, + ) + if patience is not None: + stopping.append(dict(selected_rounds=m.best_iteration, history=history)) + p = m.predict(v, raw_score=task == "A7") + if task == "A7": + p = np.exp(p + base + np.log(validation_exposure)) + model.append(m) + predictions.append(p) + prediction = np.column_stack(predictions) if task in ["A5", "A6"] else predictions[0] + elif library == "catboost": + import catboost as cb + + if task == "A8" or (task == "A10" and job["device"] == "cuda"): + raise ValueError("unsupported CatBoost task/device") + if set(cfg) != {"depth", "l2_leaf_reg"}: + raise ValueError("unsupported CatBoost parameters") + losses = { + "A1": "RMSE", + "A2": "Logloss", + "A3": "MultiClass", + "A4": "PairLogit", + "A5": "MultiQuantile:alpha=0.1,0.5,0.9", + "A6": "MultiRMSE", + "A7": "Poisson", + "A9": "Tweedie:variance_power=1.5", + "A10": "SurvivalAft:dist=Normal;scale=1", + "A11": "RMSEWithUncertainty", + "A12": "RMSE", + } + klass = ( + cb.CatBoostClassifier + if task in ["A2", "A3"] + else cb.CatBoostRanker + if task == "A4" + else cb.CatBoostRegressor + ) + target = ( + np.column_stack([y, np.where(arrays["event_train"], y, -1)]) if task == "A10" else y + ) + data = cb.Pool( + x, + label=target, + weight=None if task == "A4" else w, + group_id=arrays["query_train"] if task == "A4" else None, + group_weight=np.repeat(ranking[0][2], ranking[0][1]) if task == "A4" else None, + baseline=base + np.log(exposure) if task == "A7" else None, + ) + valid_pool = None + if patience is not None: + vt = ( + np.column_stack([vy, np.where(arrays["event_validation"], vy, -1)]) + if task == "A10" + else vy + ) + valid_pool = cb.Pool( + v, + label=vt, + weight=None if task == "A4" else vw, + group_id=arrays["query_validation"] if task == "A4" else None, + group_weight=np.repeat(ranking[1][2], ranking[1][1]) if task == "A4" else None, + baseline=base + np.log(validation_exposure) if task == "A7" else None, + ) + model = klass( + **cfg, + iterations=rounds, + learning_rate=lr, + loss_function=losses[task], + eval_metric="NDCG:top=10" if task == "A4" else None, + random_seed=seed, + thread_count=job["threads"], + task_type="GPU" if job["device"] == "cuda" else "CPU", + verbose=False, + allow_writing_files=False, + **({"boost_from_average": False, "allow_const_label": True} if task == "A6" else {}), + ).fit( + data, + eval_set=valid_pool, + early_stopping_rounds=patience, + use_best_model=patience is not None, + ) + if patience is not None: + stopping.append( + dict(selected_rounds=model.tree_count_, history=model.get_evals_result()) + ) + if task in ["A2", "A3"]: + prediction = model.predict_proba(v) + if task == "A2": + prediction = prediction[:, 1] + elif task == "A11": + prediction = model.predict(v, prediction_type="RMSEWithUncertainty") + prediction[:, 1] = np.sqrt(prediction[:, 1]) + else: + prediction = ( + model.predict(v) + if task == "A4" + else model.predict(v, prediction_type="RawFormulaVal") + ) + if task in ["A7", "A9"]: + prediction = np.exp( + prediction + (base + np.log(validation_exposure) if task == "A7" else 0) + ) + if task == "A10": + prediction = np.column_stack([prediction, np.ones(len(v))]) + else: + from ngboost import NGBRegressor + from sklearn.tree import DecisionTreeRegressor + + if task != "A11" or job["device"] != "cpu" or set(cfg) != {"weak_depth", "minibatch_frac"}: + raise ValueError("unsupported NGBoost contract") + model = NGBRegressor( + n_estimators=rounds, + learning_rate=lr, + Base=DecisionTreeRegressor(max_depth=cfg["weak_depth"], random_state=seed), + minibatch_frac=cfg["minibatch_frac"], + random_state=seed, + verbose=False, + ).fit( + x, + y, + sample_weight=w, + X_val=v if patience is not None else None, + Y_val=vy, + val_sample_weight=vw, + early_stopping_rounds=patience, + ) + if patience is not None: + prediction_rounds = model.best_val_loss_itr + 1 + stopping.append(dict(selected_rounds=prediction_rounds, history=model.evals_result)) + dist = model.pred_dist(v, max_iter=prediction_rounds) + prediction = np.column_stack([dist.loc, dist.scale]) + if target_scale is not None: + prediction = prediction * std + mean + if not np.isfinite(prediction).all() or len(prediction) != len(v): + raise ValueError("invalid output") + return prediction, { + "model": model, + "application": task, + "library": library, + "rate_base": base, + "target_scale": target_scale, + "prediction_rounds": prediction_rounds, + "stopping": stopping, + "job": job, + } + + +def predict_saved(saved, x, exposure=None): + """Replay a trusted local model bundle with its external prediction state.""" + task, library, model = saved["application"], saved["library"], saved["model"] + if task == "A7": + if exposure is None or exposure.shape != (len(x),): + raise ValueError("prediction exposure required") + if not np.isfinite(exposure).all() or np.any(exposure <= 0): + raise ValueError("positive finite prediction exposure required") + offset = saved["rate_base"] + np.log(exposure) + if library == "xgboost": + import xgboost as xgb + + data = xgb.DMatrix(x) + if task == "A7": + data.set_base_margin(offset) + result = model.predict( + data, + output_margin=task == "A10", + iteration_range=(0, saved.get("prediction_rounds") or 0), + ) + elif library == "lightgbm": + columns = [m.predict(x, raw_score=task == "A7") for m in model] + result = np.column_stack(columns) if task in ["A5", "A6"] else columns[0] + if task == "A7": + result = np.exp(result + offset) + elif library == "catboost": + if task in ["A2", "A3"]: + result = model.predict_proba(x) + if task == "A2": + result = result[:, 1] + elif task == "A11": + result = model.predict(x, prediction_type="RMSEWithUncertainty") + result[:, 1] = np.sqrt(result[:, 1]) + else: + result = ( + model.predict(x) + if task == "A4" + else model.predict(x, prediction_type="RawFormulaVal") + ) + if task in ["A7", "A9"]: + result = np.exp(result + (offset if task == "A7" else 0)) + elif library == "ngboost": + dist = model.pred_dist(x, max_iter=saved.get("prediction_rounds")) + result = np.column_stack([dist.loc, dist.scale]) + else: + raise ValueError("unknown saved baseline library") + if task == "A10": + result = np.column_stack([result, np.ones(len(x))]) + if task == "A6": + scale = saved["target_scale"] + result = result * np.asarray(scale["std"]) + np.asarray(scale["mean"]) + return result + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("job", type=Path) + args = p.parse_args() + job = json.loads(args.job.read_text()) + with np.load(job["input_npz"], allow_pickle=False) as data: + arrays = {k: data[k] for k in data.files} + prediction, model = fit(job, arrays) + payload = pickle.dumps(model) + restored = predict_saved( + pickle.loads(payload), arrays["x_validation"], arrays.get("exposure_validation") + ) + np.testing.assert_allclose(prediction, restored, rtol=1e-7, atol=1e-8) + np.savez("predictions.npz", row_ids=arrays["validation_row_ids"], prediction=prediction) + Path("model.bin").write_bytes(payload) + Path("training.json").write_text( + json.dumps( + { + "early_stopping_rounds": job.get("early_stopping_rounds"), + "prediction_rounds": model["prediction_rounds"], + "stopping": model["stopping"], + "target_scale": model["target_scale"], + }, + indent=2, + allow_nan=False, + ) + + "\n" + ) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/bike.py b/benchmarks/v1/bike.py new file mode 100644 index 0000000..7a61ce5 --- /dev/null +++ b/benchmarks/v1/bike.py @@ -0,0 +1,213 @@ +"""A5 calendar-only Bike Sharing adapter and frozen full-date rolling splits.""" + +import argparse +import csv +import hashlib +import io +import json +import platform +import subprocess +import sys +import zipfile +from dataclasses import dataclass +from datetime import date +from pathlib import Path + +import numpy as np + +SOURCE = "https://archive.ics.uci.edu/static/public/275/bike%2Bsharing%2Bdataset.zip" +ARCHIVE_SHA256 = "b70182d0d0508e9abbb79306ce5c0cec34869000f8220175ac83d11dbe845401" +MEMBER_SHA256 = "e03de4ee4ef4dc376ac6e04bf829673c6269e8eba5c60fa121640fa2f829504f" +FEATURES = ("season", "yr", "mnth", "hr", "holiday", "weekday", "workingday") +COLUMNS = ( + "instant", + "dteday", + *FEATURES[:3], + "hr", + "holiday", + "weekday", + "workingday", + "weathersit", + "temp", + "atemp", + "hum", + "windspeed", + "casual", + "registered", + "cnt", +) + + +@dataclass(frozen=True) +class BikeData: + x: np.ndarray + y: np.ndarray + row_ids: np.ndarray + dates: np.ndarray + + +def sha256(data): + return hashlib.sha256(data).hexdigest() + + +def array_hash(values, dtype): + array = np.asarray(values, dtype=dtype, order="C") + header = json.dumps( + {"dtype": array.dtype.str, "shape": list(array.shape)}, + sort_keys=True, + separators=(",", ":"), + ).encode() + return sha256(header + b"\n" + array.tobytes(order="C")) + + +def parse_hour(data): + reader = csv.DictReader(io.StringIO(data.decode("utf-8"))) + if tuple(reader.fieldnames or ()) != COLUMNS: + raise ValueError("unexpected hour.csv schema") + features, target, ids, days, timestamps = [], [], [], [], [] + for row in reader: + if None in row or any(v is None for v in row.values()): + raise ValueError("malformed CSV row") + day = date.fromisoformat(row["dteday"]) + values = [int(row[name]) for name in FEATURES] + season, year, month, hour, holiday, weekday, working = values + count, row_id = int(row["cnt"]), int(row["instant"]) + if ( + season not in range(1, 5) + or year != day.year - 2011 + or year not in (0, 1) + or month != day.month + or hour not in range(24) + or holiday not in (0, 1) + or weekday != (day.weekday() + 1) % 7 + or working != int(weekday not in (0, 6) and not holiday) + or count < 0 + or row_id <= 0 + ): + raise ValueError("invalid calendar/count/row ID") + features.append(values) + target.append(count) + ids.append(row_id) + days.append(day.isoformat()) + timestamps.append((day, hour)) + if not ids or len(set(ids)) != len(ids): + raise ValueError("empty data or duplicate row ID") + if any(a >= b for a, b in zip(timestamps, timestamps[1:], strict=False)): + raise ValueError("timestamps must be unique and chronological") + arrays = ( + np.array(features, dtype="= start) & (day_indices < end)) + for key, start, end in zip( + ("train", "validation", "test"), (0, *ends[:2]), ends, strict=True + ) + } + ) + return result + + +def describe(data): + folds = [] + for i, split in enumerate(rolling_splits(data.dates)): + parts = {} + for name, rows in split.items(): + parts[name] = { + "rows": len(rows), + "days": len(np.unique(data.dates[rows])), + "first_date": str(data.dates[rows[0]]), + "last_date": str(data.dates[rows[-1]]), + "row_ids_sha256": array_hash(data.row_ids[rows], " 0, 3.0, 0.5) + binary = (x[:, 0] > 0).astype(int) + multi = np.arange(96) % 3 + positive = np.exp(y / 2) + count = rng.poisson(positive) + rounds = 4 + cells = [] + for library in ["xgboost", "lightgbm", "catboost"]: + if library_filter is not None and library != library_filter: + continue + for task in ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11"]: + if application_filter is not None and task != application_filter: + continue + start = time.perf_counter() + record = {"library": library, "application": task, "device": device, "status": "error"} + try: + if library == "catboost" and task == "A10" and device == "cuda": + record.update( + status="unsupported", + reason="SurvivalAft GPU fit rejected by version 1.2.10; retained initial evidence", + ) + continue + if ( + (library == "lightgbm" and task in ["A10", "A11"]) + or (library == "catboost" and task == "A8") + or (library == "xgboost" and task == "A11") + ): + record.update( + status="unsupported", + reason="no matching builtin; separate outer-loop control required", + ) + continue + target = { + "A2": binary, + "A3": multi, + "A4": multi, + "A6": np.column_stack([y, 2 * y]), + "A7": count, + "A8": positive, + "A9": positive, + "A10": positive, + }.get(task, y) + with tempfile.TemporaryDirectory() as temp: + model_path = Path(temp) / "model.json" + if library == "xgboost": + objectives = { + "A1": "reg:squarederror", + "A2": "binary:logistic", + "A3": "multi:softprob", + "A4": "rank:pairwise", + "A5": "reg:quantileerror", + "A6": "reg:squarederror", + "A7": "count:poisson", + "A8": "reg:gamma", + "A9": "reg:tweedie", + "A10": "survival:aft", + } + param = { + "objective": objectives[task], + "tree_method": "hist", + "device": device, + "max_depth": 2, + "eta": 0.1, + "nthread": 2, + "seed": 41, + } + d = xgb.DMatrix(x, label=target if task != "A10" else None) + if task == "A4": + d.set_group([8] * 12) + d.set_weight(np.linspace(0.5, 2, 12)) + else: + d.set_weight(weights) + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["quantile_alpha"] = 0.5 + if task == "A6": + param["multi_strategy"] = "multi_output_tree" + if task == "A9": + param["tweedie_variance_power"] = 1.5 + if task == "A10": + d.set_float_info("label_lower_bound", positive) + d.set_float_info( + "label_upper_bound", + np.where(np.arange(96) % 4 == 0, np.inf, positive), + ) + param.update( + aft_loss_distribution="normal", aft_loss_distribution_scale=1.0 + ) + model = xgb.train(param, d, num_boost_round=rounds) + # Actual build/config is recorded; CUDA host additionally validates GPU visibility. + actual = json.loads(model.save_config())["learner"]["generic_param"][ + "device" + ] + if device == "cuda" and not actual.startswith("cuda"): + raise ValueError("silent CPU fallback") + before = model.predict(d) + model.save_model(model_path) + loaded = xgb.Booster() + loaded.load_model(model_path) + loaded.set_param({"device": device}) + after = loaded.predict(d) + if device == "cuda": + loaded.set_param({"device": "cpu"}) + cpu_prediction = loaded.predict(d) + np.testing.assert_allclose(before, cpu_prediction, rtol=1e-4, atol=1e-5) + record["cpu_inference_max_abs_error"] = float( + np.max(np.abs(before - cpu_prediction)) + ) + record["reload_device"] = device + record["effective_config"] = json.loads(model.save_config()) + elif library == "lightgbm": + objectives = { + "A1": "regression", + "A2": "binary", + "A3": "multiclass", + "A4": "lambdarank", + "A5": "quantile", + "A6": "regression", + "A7": "poisson", + "A8": "gamma", + "A9": "tweedie", + } + param = { + "objective": objectives[task], + "device_type": device, + "num_leaves": 4, + "learning_rate": 0.1, + "num_threads": 2, + "min_data_in_leaf": 2, + "verbosity": -1, + "seed": 41, + } + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["alpha"] = 0.5 + if task == "A9": + param["tweedie_variance_power"] = 1.5 + targets = target.T if task == "A6" else [target] + predictions = [] + restored = [] + for t in targets: + d = lgb.Dataset( + x, + label=t, + weight=weights + if task != "A4" + else np.repeat(np.linspace(0.5, 2, 12), 8), + group=[8] * 12 if task == "A4" else None, + ) + model = lgb.train(param, d, num_boost_round=rounds) + predictions.append(model.predict(x)) + model.save_model(str(model_path)) + restored.append(lgb.Booster(model_file=str(model_path)).predict(x)) + before = np.column_stack(predictions) if task == "A6" else predictions[0] + after = np.column_stack(restored) if task == "A6" else restored[0] + record["effective_config"] = param + else: + losses = { + "A1": "RMSE", + "A2": "Logloss", + "A3": "MultiClass", + "A4": "PairLogit", + "A5": "Quantile:alpha=0.5", + "A6": "MultiRMSE", + "A7": "Poisson", + "A9": "Tweedie:variance_power=1.5", + "A10": "SurvivalAft:dist=Normal;scale=1.0", + "A11": "RMSEWithUncertainty", + } + klass = ( + cb.CatBoostClassifier + if task in ["A2", "A3"] + else cb.CatBoostRanker + if task == "A4" + else cb.CatBoostRegressor + ) + model = klass( + loss_function=losses[task], + iterations=rounds, + depth=2, + learning_rate=0.1, + thread_count=2, + random_seed=41, + task_type="GPU" if device == "cuda" else "CPU", + verbose=False, + allow_writing_files=False, + ) + if task == "A10": + target = np.column_stack( + [positive, np.where(np.arange(96) % 4 == 0, -1, positive)] + ) + if task == "A4": + d = cb.Pool( + x, + target, + group_id=np.repeat(np.arange(12), 8), + group_weight=np.repeat(np.linspace(0.5, 2, 12), 8), + ) + else: + d = cb.Pool(x, target, weight=weights) + model.fit(d) + before = ( + model.predict(d) + if task == "A4" + else model.predict(d, prediction_type="RawFormulaVal") + ) + model.save_model(str(model_path)) + loaded = klass() + loaded.load_model(str(model_path)) + after = ( + loaded.predict(d) + if task == "A4" + else loaded.predict(d, prediction_type="RawFormulaVal") + ) + record["effective_config"] = model.get_all_params() + if not np.isfinite(before).all() or len(before) != 96: + raise ValueError("invalid predictions") + np.testing.assert_allclose(before, after, rtol=1e-7, atol=1e-8) + record.update( + status="pass", + prediction_shape=list(np.shape(before)), + reload_max_abs_error=float(np.max(np.abs(before - after))), + ) + except Exception as exc: + record.update(status="error", reason=f"{type(exc).__name__}: {exc}") + finally: + record["diagnostic_wall_s"] = time.perf_counter() - start + cells.append(record) + return { + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates", + "device": device, + "environment": { + "python": platform.python_version(), + "os": platform.platform(), + "cpu": platform.processor(), + "cpu_count": os.cpu_count(), + "packages": {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + }, + "cells": cells, + } + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--device", choices=["cpu", "cuda"], default="cpu") + p.add_argument("--output", type=Path, required=True) + a = p.parse_args() + result = run(a.device) + result["source_sha"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + result["dirty"] = bool(subprocess.check_output(["git", "status", "--porcelain"])) + result["source_file_sha256"] = ( + __import__("hashlib").sha256(Path(__file__).read_bytes()).hexdigest() + ) + a.output.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + print([(r["library"], r["application"], r["status"]) for r in result["cells"]]) + return int(any(r["status"] == "error" for r in result["cells"])) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/v1/current_selection_smoke.py b/benchmarks/v1/current_selection_smoke.py new file mode 100644 index 0000000..b93edd2 --- /dev/null +++ b/benchmarks/v1/current_selection_smoke.py @@ -0,0 +1,196 @@ +"""Synthetic current A6 search, scale-bound audit and selected-model replay.""" + +import argparse +import hashlib +import importlib.metadata +import itertools +import json +import os +import platform +import subprocess +import sys +from pathlib import Path + +import numpy as np + +from benchmarks.v1.preprocessing import fit_target_scale +from benchmarks.v1.process_runner import execute +from benchmarks.v1.selection import audit, digest, release_test, seal + + +def run(directory): + root = Path(directory).resolve() + root.mkdir(parents=True, exist_ok=True) + if any(root.iterdir()): + raise ValueError("fresh output directory required") + repo = Path(__file__).resolve().parents[2] + rng = np.random.default_rng(46) + x = rng.normal(size=(96, 3)) + y = np.column_stack([100 + 20 * x[:, 0], -300 + 0.01 * x[:, 1], np.full(96, 7.0)]) + np.savez(root / "train-rows.npz", row_ids=np.arange(64)) + np.savez(root / "train-targets.npz", row_ids=np.arange(64), y=y[:64]) + np.savez(root / "validation.npz", row_ids=np.arange(64, 80), y=y[64:80]) + np.savez(root / "test-features.npz", row_ids=np.arange(80, 96), x=x[80:]) + np.savez( + root / "worker-input.npz", + x_train=x[:64], + y_train=y[:64], + x_validation=x[64:80], + y_validation=y[64:80], + validation_row_ids=np.arange(64, 80), + ) + scale = fit_target_scale(y[:64]) + (root / "scale.json").write_text(json.dumps(scale, indent=2) + "\n") + + def entry(path): + return dict( + path=str(path.relative_to(root)), sha256=hashlib.sha256(path.read_bytes()).hexdigest() + ) + + configs = [ + dict(rounds=4, learning_rate=rate, max_depth=depth, mode=mode, bins=16, reg_lambda=1.0) + for mode, rate, depth in itertools.product( + ("shared", "independent"), (0.05, 0.1), (1, 2, 3, 4) + ) + ] + sources = { + str(p.relative_to(repo)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in sorted((repo / "src/openboost").rglob("*.py")) + } + for name in ( + "current_selection_smoke.py", + "openboost_worker.py", + "openboost_predict.py", + "selection.py", + "preprocessing.py", + "quality.py", + "quality_report.py", + "process_runner.py", + ): + p = Path(__file__).with_name(name) + sources[str(p.relative_to(repo))] = hashlib.sha256(p.read_bytes()).hexdigest() + environment = dict( + python=platform.python_version(), + os=platform.platform(), + threads=1, + cpu_count=os.cpu_count(), + gpu=None, + memory_cap=None, + packages={n: importlib.metadata.version(n) for n in ("openboost", "numpy")}, + ) + protocol = dict( + schema="openboost-selection-v1", + application="A6", + fold=0, + identity=dict( + code=digest(sources), + data=entry(root / "worker-input.npz")["sha256"], + split=digest([64, 80, 96]), + preprocessing=digest(scale), + environment=digest(environment), + search_design=digest(configs), + ), + train_rows=entry(root / "train-rows.npz"), + train_targets=entry(root / "train-targets.npz"), + target_scale=entry(root / "scale.json"), + validation=entry(root / "validation.npz"), + test_features=entry(root / "test-features.npz"), + selection_weights={f"rmse_{k}": 1 / std for k, std in enumerate(scale["std"])}, + methods={"openboost": configs}, + ) + pinned = digest(protocol) + (root / "protocol.json").write_text(json.dumps(protocol, indent=2) + "\n") + report = dict( + scope="Synthetic A6/A13 selection integration only; not real quality, cost or OS isolation", + revision=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + argv=[sys.executable, "-m", "benchmarks.v1.current_selection_smoke", str(root)], + sources=sources, + environment=environment, + protocol_sha256=pinned, + passed=False, + trials=[], + ) + records = [] + try: + for index, config in enumerate(configs): + job = dict( + application="A6", + library="openboost", + device="cpu", + threads=1, + seed=46, + config=config, + early_stopping_rounds=2, + input_npz=str(root / "worker-input.npz"), + ) + job_path = root / f"job-{index}.json" + job_path.write_text(json.dumps(job, indent=2) + "\n") + out = root / f"trial-{index}" + outcome = execute( + [sys.executable, str(repo / "benchmarks/v1/openboost_worker.py"), str(job_path)], + out, + timeout_s=60, + threads=1, + ) + report["trials"].append(outcome) + record = dict( + id=f"openboost:{index:02}", + config=config, + status=outcome["status"], + exit_code=outcome["exit_code"], + protocol_sha256=pinned, + prediction=entry(out / "predictions.npz") + if (out / "predictions.npz").exists() + else None, + model=entry(out / "model.bin") if (out / "model.bin").exists() else None, + log=entry(out / "execution.json"), + ) + records.append(record) + (root / "records.json").write_text(json.dumps(records, indent=2) + "\n") + receipt = audit(protocol, records, root, pinned) + receipt_hash = seal(receipt, root / "receipt.json") + features, selected = release_test( + protocol, records, root / "receipt.json", root, pinned, receipt_hash + ) + # Verify the exact selected bytes again before spawning inference. + model = root / selected["path"] + assert hashlib.sha256(model.read_bytes()).hexdigest() == selected["sha256"] + np.savez(root / "released-features.npz", **features) + command = [ + sys.executable, + str(repo / "benchmarks/v1/openboost_predict.py"), + str(model), + str(root / "released-features.npz"), + str(root / "selected-predictions.npz"), + ] + subprocess.run( + command, + cwd=root, + check=True, + timeout=30, + env=dict( + os.environ, OMP_NUM_THREADS="1", OPENBLAS_NUM_THREADS="1", MKL_NUM_THREADS="1" + ), + ) + with np.load(root / "selected-predictions.npz") as result: + assert result["prediction"].shape == (16, 3) and np.isfinite(result["prediction"]).all() + np.testing.assert_array_equal(result["row_ids"], features["row_ids"]) + np.testing.assert_array_equal(result["prediction"][:, 2], np.full(16, 7.0)) + report.update( + passed=True, + selected=receipt["selected"], + receipt_sha256=receipt_hash, + inference_command=command, + selected_predictions=entry(root / "selected-predictions.npz"), + ) + finally: + (root / "summary.json").write_text(json.dumps(report, indent=2, allow_nan=False) + "\n") + return report + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + args = parser.parse_args() + print(run(args.directory)["selected"]) diff --git a/benchmarks/v1/datasets/adult.json b/benchmarks/v1/datasets/adult.json new file mode 100644 index 0000000..ec6c922 --- /dev/null +++ b/benchmarks/v1/datasets/adult.json @@ -0,0 +1,246 @@ +{ + "application": "A2", + "archive_sha256": "7537312dd56c2b98035880805ce99e68183a30ee468aa5329d6df0fbb3cc21bb", + "categorical": [ + "workclass", + "education", + "marital-status", + "occupation", + "relationship", + "race", + "sex", + "native-country" + ], + "citation": "Becker, B. & Kohavi, R. (1996). Adult. UCI. DOI:10.24432/C5XW20", + "features": [ + "age", + "workclass", + "education", + "education-num", + "marital-status", + "occupation", + "relationship", + "race", + "sex", + "capital-gain", + "capital-loss", + "hours-per-week", + "native-country" + ], + "folds": [ + { + "seed": 0, + "test": { + "class_counts": [ + 12435, + 3846 + ], + "row_ids_sha256": "9c503b617d3a3d7b6cb9ab198129bcaafcdf1fe29b0c246e93e457d63b64dc51", + "rows": 16281 + }, + "train": { + "class_counts": [ + 19776, + 6272 + ], + "row_ids_sha256": "ef57d48b3dc4f5d042c247836af5c6303497bda250a03ea458fcf730557962fd", + "rows": 26048 + }, + "validation": { + "class_counts": [ + 4944, + 1569 + ], + "row_ids_sha256": "6fb7f2820606406639d874ce5460fbfed6ca5e542354b4c02f1ce098e14d1019", + "rows": 6513 + } + }, + { + "seed": 1, + "test": { + "class_counts": [ + 12435, + 3846 + ], + "row_ids_sha256": "9c503b617d3a3d7b6cb9ab198129bcaafcdf1fe29b0c246e93e457d63b64dc51", + "rows": 16281 + }, + "train": { + "class_counts": [ + 19776, + 6272 + ], + "row_ids_sha256": "0b7be8aea92bc54fd863195d0b20b84886327d3c946e38d58e83445329572dd7", + "rows": 26048 + }, + "validation": { + "class_counts": [ + 4944, + 1569 + ], + "row_ids_sha256": "8e2f2ced4a68efe047202be0055e315c2a0c7e6a4d75ec1f88798a1fa131105e", + "rows": 6513 + } + }, + { + "seed": 2, + "test": { + "class_counts": [ + 12435, + 3846 + ], + "row_ids_sha256": "9c503b617d3a3d7b6cb9ab198129bcaafcdf1fe29b0c246e93e457d63b64dc51", + "rows": 16281 + }, + "train": { + "class_counts": [ + 19776, + 6272 + ], + "row_ids_sha256": "71a251c9447ee0bc3e97c1756b0ec0256017eda108fa2240d91b9876398df70f", + "rows": 26048 + }, + "validation": { + "class_counts": [ + 4944, + 1569 + ], + "row_ids_sha256": "0bd4e39e464d933925a216e29e703a4feedf4fde268c55859a12499086fab194", + "rows": 6513 + } + }, + { + "seed": 3, + "test": { + "class_counts": [ + 12435, + 3846 + ], + "row_ids_sha256": "9c503b617d3a3d7b6cb9ab198129bcaafcdf1fe29b0c246e93e457d63b64dc51", + "rows": 16281 + }, + "train": { + "class_counts": [ + 19776, + 6272 + ], + "row_ids_sha256": "b5734005f8e877367065c95b08813553e5e2dcae59cfdc44e2807ab96c5d0586", + "rows": 26048 + }, + "validation": { + "class_counts": [ + 4944, + 1569 + ], + "row_ids_sha256": "8cff4f624aece5b82112230b660d6204c9588a6c9f368dd2befca97ae39acfed", + "rows": 6513 + } + }, + { + "seed": 4, + "test": { + "class_counts": [ + 12435, + 3846 + ], + "row_ids_sha256": "9c503b617d3a3d7b6cb9ab198129bcaafcdf1fe29b0c246e93e457d63b64dc51", + "rows": 16281 + }, + "train": { + "class_counts": [ + 19776, + 6272 + ], + "row_ids_sha256": "3a82bd9046a0ac1064fa553b0dfb52f892ce8471c7ea07eaf0ce26f2f0646190", + "rows": 26048 + }, + "validation": { + "class_counts": [ + 4944, + 1569 + ], + "row_ids_sha256": "d723e20e41379e967b38c7e683123b57fbbff207137462a2db23e8c04f662891", + "rows": 6513 + } + } + ], + "hash_format": "SHA256(canonical JSON with sorted keys and compact separators; ensure_ascii=True)", + "identity_note": "source:physical-line IDs; repeated predictor rows retained; no person IDs available", + "labels": [ + "<=50K", + ">50K" + ], + "license": "CC-BY-4.0", + "license_source": "https://archive.ics.uci.edu/dataset/2/adult", + "member_sha256": { + "adult.data": "5b00264637dbfec36bdeaab5676b0b309ff9eb788d63554ca0a249491c86603d", + "adult.test": "a2a9044bc167a35b2361efbabec64e89d69ce82d9790d2980119aac5fd7e9c05" + }, + "numeric": [ + "age", + "education-num", + "capital-gain", + "capital-loss", + "hours-per-week" + ], + "provenance": { + "adapter_sha256": "9766e4323e018fbdca1182b5ff89cb154336d5e1c48120382a8b6a38513f626f", + "argv": [ + "python", + "-m", + "benchmarks.v1.adult", + "/tmp/openboost-v1-adult.zip" + ], + "dirty": true, + "git_sha": "f74df584189b59f5248c804ccdd517527db532c5", + "numpy": "2.3.5", + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12" + }, + "schema": "openboost-adult-data-v1", + "source_records": { + "adult.data": { + "data_sha256": "bfc371119520d55a7c7404d2b2d049207c0721150ee6b42dc4262a36314adb07", + "missing_by_feature": [ + 0, + 1836, + 0, + 0, + 0, + 1843, + 0, + 0, + 0, + 0, + 0, + 0, + 583 + ], + "rows": 32561 + }, + "adult.test": { + "data_sha256": "6e29d2959bacd2b57fa02eeec8ee852349260934f2846c9608db3ff4342875ca", + "missing_by_feature": [ + 0, + 963, + 0, + 0, + 0, + 966, + 0, + 0, + 0, + 0, + 0, + 0, + 274 + ], + "rows": 16281 + } + }, + "source_url": "https://archive.ics.uci.edu/static/public/2/adult.zip", + "split_rule": "official test unchanged; per seed PCG64 default_rng, class 0 then 1 permutation; floor(0.8*n_class) train, rest validation; sort source indices", + "status": "raw data/splits only; numeric encoding, budgets and quality pending", + "test_rows_with_predictors_seen_in_official_train": 2979, + "weight": "unit; fnlwgt excluded" +} diff --git a/benchmarks/v1/datasets/bike.json b/benchmarks/v1/datasets/bike.json new file mode 100644 index 0000000..3e0a7dc --- /dev/null +++ b/benchmarks/v1/datasets/bike.json @@ -0,0 +1,182 @@ +{ + "application": "A5", + "archive_sha256": "b70182d0d0508e9abbb79306ce5c0cec34869000f8220175ac83d11dbe845401", + "array_hash_format": "SHA256(canonical JSON dtype/shape + newline + C-order little-endian bytes)", + "citation": "Fanaee-T, H. (2013). Bike Sharing. UCI. DOI:10.24432/C5W894", + "days": 731, + "features": [ + "season", + "yr", + "mnth", + "hr", + "holiday", + "weekday", + "workingday" + ], + "folds": [ + { + "origin": 0, + "splits": { + "test": { + "days": 73, + "first_date": "2012-03-14", + "last_date": "2012-05-25", + "row_ids_sha256": "01641b69ddeb5d6fb61a7835d0b98e6f299a4f5072e6b9f033082a25fda34fc2", + "rows": 1750 + }, + "train": { + "days": 365, + "first_date": "2011-01-01", + "last_date": "2011-12-31", + "row_ids_sha256": "b3b37fee310037fd2e67a4794afb86b4c3b7636f16f970313b2567f259889723", + "rows": 8645 + }, + "validation": { + "days": 73, + "first_date": "2012-01-01", + "last_date": "2012-03-13", + "row_ids_sha256": "94477640fbb26645d43e3fdfacecb82473e2cb68e9861286cad29e35add22ea5", + "rows": 1744 + } + }, + "training_percent": 50 + }, + { + "origin": 1, + "splits": { + "test": { + "days": 73, + "first_date": "2012-04-20", + "last_date": "2012-07-01", + "row_ids_sha256": "af78aebec39e7d94d01a1825c342216038443251deebaa8c807062193ac9f393", + "rows": 1752 + }, + "train": { + "days": 402, + "first_date": "2011-01-01", + "last_date": "2012-02-06", + "row_ids_sha256": "71bb5c378ee0c5a56da258ab573346ab0bdb66ef2ce203dcfba5e0480a649cec", + "rows": 9529 + }, + "validation": { + "days": 73, + "first_date": "2012-02-07", + "last_date": "2012-04-19", + "row_ids_sha256": "aad05607be6ca10328f34b2bfda58c160d88110411deae6f5eb9de20e84d9d58", + "rows": 1746 + } + }, + "training_percent": 55 + }, + { + "origin": 2, + "splits": { + "test": { + "days": 73, + "first_date": "2012-05-26", + "last_date": "2012-08-06", + "row_ids_sha256": "b64290b18822aecd5f200e5b09ef29a77ba375e45caa5d6ed53d5cd00c526405", + "rows": 1752 + }, + "train": { + "days": 438, + "first_date": "2011-01-01", + "last_date": "2012-03-13", + "row_ids_sha256": "c35939a6bc4f413d1b11cfa81e31cc93453af119d8e2ccf5bffd67fb5e1b1503", + "rows": 10389 + }, + "validation": { + "days": 73, + "first_date": "2012-03-14", + "last_date": "2012-05-25", + "row_ids_sha256": "01641b69ddeb5d6fb61a7835d0b98e6f299a4f5072e6b9f033082a25fda34fc2", + "rows": 1750 + } + }, + "training_percent": 60 + }, + { + "origin": 3, + "splits": { + "test": { + "days": 73, + "first_date": "2012-07-02", + "last_date": "2012-09-12", + "row_ids_sha256": "e6f1cd45938811e7433802de270acb6bfa094152fed88dd2f400f3b722254a61", + "rows": 1752 + }, + "train": { + "days": 475, + "first_date": "2011-01-01", + "last_date": "2012-04-19", + "row_ids_sha256": "ed7861418fa1f6ec2032743f24775c0c45f6516aac481dd7dbdd8d261c6e99de", + "rows": 11275 + }, + "validation": { + "days": 73, + "first_date": "2012-04-20", + "last_date": "2012-07-01", + "row_ids_sha256": "af78aebec39e7d94d01a1825c342216038443251deebaa8c807062193ac9f393", + "rows": 1752 + } + }, + "training_percent": 65 + }, + { + "origin": 4, + "splits": { + "test": { + "days": 73, + "first_date": "2012-08-07", + "last_date": "2012-10-18", + "row_ids_sha256": "fc8608846bb4fe4d5f064d1e4ac2d2873c5262f930fbad25856ec2af91302f9a", + "rows": 1752 + }, + "train": { + "days": 511, + "first_date": "2011-01-01", + "last_date": "2012-05-25", + "row_ids_sha256": "57f39fed5e2691be9dcdb3c01496b700feff15396f6af814dd86b9f306215fa0", + "rows": 12139 + }, + "validation": { + "days": 73, + "first_date": "2012-05-26", + "last_date": "2012-08-06", + "row_ids_sha256": "b64290b18822aecd5f200e5b09ef29a77ba375e45caa5d6ed53d5cd00c526405", + "rows": 1752 + } + }, + "training_percent": 70 + } + ], + "license": "CC-BY-4.0", + "license_source": "https://archive.ics.uci.edu/dataset/275/bike+sharing+dataset", + "member": "hour.csv", + "member_sha256": "e03de4ee4ef4dc376ac6e04bf829673c6269e8eba5c60fa121640fa2f829504f", + "provenance": { + "adapter_sha256": "e8fe271c02d7ccc21765b08b9237b9d80418d8960fd49da3cad7157f7ca95441", + "argv": [ + "python", + "-m", + "benchmarks.v1.bike", + "/tmp/openboost-v1-bike.zip" + ], + "dirty": true, + "git_sha": "e2f2c6bc8149740208242ed3e899d072398917ce", + "numpy": "2.3.5", + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12" + }, + "row_ids_sha256": "0207a9c39b3e957b17ca0459006bc38a40baa6d3faec588dc470903ed17d22f9", + "rows": 17379, + "schema": "openboost-bike-data-v1", + "source_url": "https://archive.ics.uci.edu/static/public/275/bike%2Bsharing%2Bdataset.zip", + "split_rule": "sorted unique dates; floor(D*p/100) endpoints; expanding train, next 10% validation, next 10% test", + "status": "data prepared only; no quality or budget gate evaluated", + "target": "cnt", + "target_unit": "hourly rental count", + "weight": "unit", + "x_sha256": "d0e1ba087d4ed7e2a3e1f59c95e072b9efa389e1699d670b2e2c5dc16f94276b", + "y_sha256": "bdad86e5f4f2323cf66c409915ace4308eef0424a7b010cec7017162ecd08a5b" +} diff --git a/benchmarks/v1/datasets/concrete.json b/benchmarks/v1/datasets/concrete.json new file mode 100644 index 0000000..a89b013 --- /dev/null +++ b/benchmarks/v1/datasets/concrete.json @@ -0,0 +1,208 @@ +{ + "adapter_sha256": "e796be3be35202fe688235b40798ba98793605adcdcd6194f2729fb869eac0b9", + "arrays": { + "group": { + "sha256": "29e032393b1cdfbf440f396ffc48039988b7f477d3b84fc45a10cb9ba072a193", + "shape": [ + 1030 + ] + }, + "structure": { + "sha256": "f560f09f23a1d627c5d512a3ba830f0568d7985ddd2a18f4b703b0e38af42f66", + "shape": [ + 1030 + ] + }, + "x": { + "sha256": "9586114252b2366e65c3b3b761de7079dffa574f3a784b9d11aa0f0f40d1d8f8", + "shape": [ + 1030, + 7 + ] + }, + "y": { + "sha256": "93f0801667b9aae6d030a31952725bf08d01aaad24236c15464730de2b7dd6e3", + "shape": [ + 1030 + ] + } + }, + "audit": {}, + "dataset": "concrete", + "folds": [ + { + "seed": 0, + "test": { + "age_days_min_max": [ + 1.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "58e094e84ddc124a2db9858629267bd98f420a163ff27c018e85456f45f530ba", + "rows": 197 + }, + "test_age_outside_train_support": 0, + "train": { + "age_days_min_max": [ + 1.0, + 365.0 + ], + "groups": 256, + "row_indices_sha256": "a6c16f3c3e16a937d0612e6a30440c317d0b772f7c3e8a1a213621d5a76a816e", + "rows": 646 + }, + "validation": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "2c3beeb63a0de7ec9882ac33c62b4d17b485ea74b1d56f5f80cd225c462b2f9c", + "rows": 187 + } + }, + { + "seed": 1, + "test": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "21bea9fe4568b8d9770c12e0aaef86c8d9d4ef04406d156a510f7ae63d3a0fdc", + "rows": 196 + }, + "test_age_outside_train_support": 0, + "train": { + "age_days_min_max": [ + 1.0, + 365.0 + ], + "groups": 256, + "row_indices_sha256": "3a9105e6cf0929caa6ba80a67cb2d32eddd3503c2a3d79ab8f12466c8abc452c", + "rows": 633 + }, + "validation": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "c488547459f14352a654f97d9e7c1f38714f18ece389a7436e6fa0b7198300bd", + "rows": 201 + } + }, + { + "seed": 2, + "test": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "f063d20496f7f3f511f576a55932a4b5bb847036acf29cb5c9e34e5fd678e702", + "rows": 227 + }, + "test_age_outside_train_support": 0, + "train": { + "age_days_min_max": [ + 1.0, + 365.0 + ], + "groups": 256, + "row_indices_sha256": "76ab743dd73b53b174e0e3ae3644caf932f38812de470fa47605b4d7eff1dd75", + "rows": 588 + }, + "validation": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "d66d2418044c1002a88c2e7355030c72bd2139a06f0043e7a7284b93808d3b9f", + "rows": 215 + } + }, + { + "seed": 3, + "test": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "3663281bc4e54a83c8e22184515bf7ed5b06dcb085994170168593bb267b37eb", + "rows": 200 + }, + "test_age_outside_train_support": 0, + "train": { + "age_days_min_max": [ + 1.0, + 365.0 + ], + "groups": 256, + "row_indices_sha256": "ee2b2b66aec3006780a1e3685347b43b1f236406440ab8e8af26b9e6633c944c", + "rows": 613 + }, + "validation": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "355f77bcf1ca8aa3bd575eadc57b7ac84899a4a4544d90106fe2b379913b8ddd", + "rows": 217 + } + }, + { + "seed": 4, + "test": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "e0383c21377c7a33b333edb099944d8c0f2bf001386313961e4299dd72800417", + "rows": 185 + }, + "test_age_outside_train_support": 0, + "train": { + "age_days_min_max": [ + 1.0, + 365.0 + ], + "groups": 256, + "row_indices_sha256": "bc2b7a62c398b676f8994a50be35247d51146d7e0f69d0ca8e5b6b5d75962afd", + "rows": 634 + }, + "validation": { + "age_days_min_max": [ + 3.0, + 365.0 + ], + "groups": 86, + "row_indices_sha256": "3f50728184da9977b7dae16802936c52c4e85ca34ec9b816d617707f21447aac", + "rows": 211 + } + } + ], + "hash_format": "SHA256(JSON [little-endian dtype,shape] compact + newline + contiguous C-order bytes)", + "provenance": { + "argv": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "-m", + "benchmarks.v1.real_data", + "concrete", + "build/v1-data" + ], + "dirty": true, + "git_sha": "dc744016687fde2c3347cc4e83c9c9f9e0a0aee8", + "numpy": "2.3.5", + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12" + }, + "schema": "openboost-real-data-v1", + "sources_sha256": "fb95ff2763ca4dcee54e46e9d201c1508d3a2ed3a80e366b1058dfed1d3d26e6", + "split_rule": "group; sorted unique groups/classes; PCG64(seed); floor 60/80 percent boundaries; sorted source rows", + "status": "data preparation only; no model quality claim" +} diff --git a/benchmarks/v1/datasets/covertype.json b/benchmarks/v1/datasets/covertype.json new file mode 100644 index 0000000..d5fb99f --- /dev/null +++ b/benchmarks/v1/datasets/covertype.json @@ -0,0 +1,251 @@ +{ + "adapter_sha256": "e796be3be35202fe688235b40798ba98793605adcdcd6194f2729fb869eac0b9", + "arrays": { + "x": { + "sha256": "e5a03c7f75ddaa430e03455b41409f23b79841395153f1f34ef50231a0635ef3", + "shape": [ + 581012, + 54 + ] + }, + "y": { + "sha256": "44b06118bfb241f18b573ff940ccd3768365517036e4d82ab6c8208bf715ee4d", + "shape": [ + 581012 + ] + } + }, + "audit": {}, + "dataset": "covertype", + "folds": [ + { + "seed": 0, + "test": { + "class_counts": [ + 42368, + 56661, + 7151, + 550, + 1899, + 3474, + 4102 + ], + "row_indices_sha256": "f750de655b125228a081560fa27c88fd393da0926d22b5afcbac4314d1e737aa", + "rows": 116205 + }, + "train": { + "class_counts": [ + 127104, + 169980, + 21452, + 1648, + 5695, + 10420, + 12306 + ], + "row_indices_sha256": "1d112fb94bd2c53bd669f1bf442aa9f5d56e1ec92d1e8b991b651e0a8cc6b169", + "rows": 348605 + }, + "validation": { + "class_counts": [ + 42368, + 56660, + 7151, + 549, + 1899, + 3473, + 4102 + ], + "row_indices_sha256": "e8fcb0bdd16ef52181f0f5f492bcbcebdab517bc3e9205aa6654319769f921bf", + "rows": 116202 + } + }, + { + "seed": 1, + "test": { + "class_counts": [ + 42368, + 56661, + 7151, + 550, + 1899, + 3474, + 4102 + ], + "row_indices_sha256": "a04ff05c9574f7210e8608dc90a19934f8fcabe77d425d1a74e81f3fc57eb759", + "rows": 116205 + }, + "train": { + "class_counts": [ + 127104, + 169980, + 21452, + 1648, + 5695, + 10420, + 12306 + ], + "row_indices_sha256": "631a78a1ae71a71a138d898e81d4d18d3c445147faa0c9668ed2c3a4bd4f1190", + "rows": 348605 + }, + "validation": { + "class_counts": [ + 42368, + 56660, + 7151, + 549, + 1899, + 3473, + 4102 + ], + "row_indices_sha256": "4dd256c294064f6162b89d3207fb09d7ae588971addfca7363c40477eae266ac", + "rows": 116202 + } + }, + { + "seed": 2, + "test": { + "class_counts": [ + 42368, + 56661, + 7151, + 550, + 1899, + 3474, + 4102 + ], + "row_indices_sha256": "1c1c6c1eb94737843529cf1a51853bbfb8436e91433042dfb10354f0b710e04a", + "rows": 116205 + }, + "train": { + "class_counts": [ + 127104, + 169980, + 21452, + 1648, + 5695, + 10420, + 12306 + ], + "row_indices_sha256": "b6e1a87c13974d9c911195c9dc096045d03b9e8b6cf11c3fc46d6e6772e17990", + "rows": 348605 + }, + "validation": { + "class_counts": [ + 42368, + 56660, + 7151, + 549, + 1899, + 3473, + 4102 + ], + "row_indices_sha256": "61625c39c1c7810b759014538333935d0813505baddc5e21ada535b0b12db954", + "rows": 116202 + } + }, + { + "seed": 3, + "test": { + "class_counts": [ + 42368, + 56661, + 7151, + 550, + 1899, + 3474, + 4102 + ], + "row_indices_sha256": "bf9d5e3e6616afa68419a58b548666d78095b277559689206648fa88f84bed7f", + "rows": 116205 + }, + "train": { + "class_counts": [ + 127104, + 169980, + 21452, + 1648, + 5695, + 10420, + 12306 + ], + "row_indices_sha256": "096f81f9986fec6a252174c5eb1d01d4f97ccc3f6cbc1d0d034404efe48c3a1d", + "rows": 348605 + }, + "validation": { + "class_counts": [ + 42368, + 56660, + 7151, + 549, + 1899, + 3473, + 4102 + ], + "row_indices_sha256": "a031130a460870d066f813afec32b5c4e82a5a7d7e4e71e7e2486130ac152afe", + "rows": 116202 + } + }, + { + "seed": 4, + "test": { + "class_counts": [ + 42368, + 56661, + 7151, + 550, + 1899, + 3474, + 4102 + ], + "row_indices_sha256": "6ac7beb70dfd54b9a84f16ba42591402fbdf608a6999255d63efe45ecc75443c", + "rows": 116205 + }, + "train": { + "class_counts": [ + 127104, + 169980, + 21452, + 1648, + 5695, + 10420, + 12306 + ], + "row_indices_sha256": "906fc81fc385f9dc6de461ee07f75dce258648a5338f07a121eb7fe0d7a38b9c", + "rows": 348605 + }, + "validation": { + "class_counts": [ + 42368, + 56660, + 7151, + 549, + 1899, + 3473, + 4102 + ], + "row_indices_sha256": "29816021c8e3e1232037c0f81051f2eae92c7c81b4262316bc96d8c0348f7493", + "rows": 116202 + } + } + ], + "hash_format": "SHA256(JSON [little-endian dtype,shape] compact + newline + contiguous C-order bytes)", + "provenance": { + "argv": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "-m", + "benchmarks.v1.real_data", + "covertype", + "build/v1-data" + ], + "dirty": true, + "git_sha": "dc744016687fde2c3347cc4e83c9c9f9e0a0aee8", + "numpy": "2.3.5", + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12" + }, + "schema": "openboost-real-data-v1", + "sources_sha256": "fb95ff2763ca4dcee54e46e9d201c1508d3a2ed3a80e366b1058dfed1d3d26e6", + "split_rule": "stratified; sorted unique groups/classes; PCG64(seed); floor 60/80 percent boundaries; sorted source rows", + "status": "data preparation only; no model quality claim" +} diff --git a/benchmarks/v1/datasets/housing-license-review.json b/benchmarks/v1/datasets/housing-license-review.json new file mode 100644 index 0000000..85c34ed --- /dev/null +++ b/benchmarks/v1/datasets/housing-license-review.json @@ -0,0 +1,99 @@ +{ + "schema": "openboost-source-license-review-v1", + "review_date": "2026-09-06", + "source_url": "https://api.figshare.com/v2/articles/3829992", + "retrieved_response_sha256": "98ce5e5d2e5ba16a0406e60602e7e48608e7ad1cc4dc45ae8327f24879dde7c2", + "omitted_fields": [ + "url_private_api", + "url_private_html" + ], + "metadata": { + "files": [ + { + "id": 5976036, + "name": "cal_housing.tgz", + "size": 441963, + "is_link_only": false, + "download_url": "https://ndownloader.figshare.com/files/5976036", + "supplied_md5": "130d0eececf165046ec4dc621d121d80", + "computed_md5": "130d0eececf165046ec4dc621d121d80", + "mimetype": "application/x-gzip" + } + ], + "folder_structure": {}, + "authors": [ + { + "id": 3118605, + "full_name": "Nelson Liu", + "first_name": "Nelson", + "last_name": "Liu", + "is_active": true, + "url_name": "_", + "orcid_id": "" + } + ], + "custom_fields": [], + "figshare_url": "https://figshare.com/articles/dataset/cal_housing_tgz/3829992", + "download_disabled": false, + "description": "Part of the California housing dataset used in scikit-learn.", + "funding": null, + "funding_list": [], + "version": 2, + "status": "public", + "size": 441963, + "created_date": "2016-09-15T19:33:05Z", + "modified_date": "2016-09-15T19:33:08Z", + "is_public": true, + "is_confidential": false, + "is_metadata_record": false, + "confidential_reason": "", + "metadata_reason": "", + "license": { + "value": 1, + "name": "CC BY 4.0", + "url": "https://creativecommons.org/licenses/by/4.0/" + }, + "tags": [ + "california", + "housing", + "scikit-learn" + ], + "keywords": [ + "california", + "housing", + "scikit-learn" + ], + "categories": [], + "references": [], + "has_linked_file": false, + "citation": "Liu, Nelson (2016). scikit-learn california housing dataset cal_housing.tgz. figshare. Dataset. https://doi.org/10.6084/m9.figshare.3829992.v2", + "related_materials": [], + "is_embargoed": false, + "embargo_date": null, + "embargo_type": "file", + "embargo_title": "", + "embargo_reason": "", + "embargo_options": [], + "id": 3829992, + "title": "scikit-learn california housing dataset cal_housing.tgz", + "doi": "10.6084/m9.figshare.3829992.v2", + "handle": "", + "url": "https://api.figshare.com/v2/articles/3829992", + "published_date": "2016-09-15T19:33:04Z", + "thumb": "", + "defined_type": 3, + "defined_type_name": "dataset", + "group_id": null, + "url_public_api": "https://api.figshare.com/v2/articles/3829992", + "url_public_html": "https://figshare.com/articles/dataset/cal_housing_tgz/3829992", + "timeline": { + "posted": "2016-09-15T19:33:04", + "firstOnline": "2016-09-15" + }, + "resource_title": null, + "resource_doi": null + }, + "archive_sha256": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681", + "archive_md5_verified": true, + "conclusion": "The exact hosted archive has a CC BY 4.0 declaration. This supplements the original freeze without changing bytes, folds, or preprocessing. The declaration is by the Figshare uploader; original StatLib rights provenance was not separately verified." +} diff --git a/benchmarks/v1/datasets/housing.json b/benchmarks/v1/datasets/housing.json new file mode 100644 index 0000000..baf9bd0 --- /dev/null +++ b/benchmarks/v1/datasets/housing.json @@ -0,0 +1,82 @@ +{ + "applications": [ + "A1", + "A11" + ], + "archive_sha256": "aaa5c9a6afe2225cc2aed2723682ae403280c4a3695a2ddda4ffb5d8215ea681", + "array_hash_format": "SHA256(X C-order + main() + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 150, in main + prediction, saved, training = fit(job, arrays) + ^^^^^^^^^^^^^^^^ + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 91, in fit + data = NumericData(x, ids, names) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 7, in __init__ + File "/Users/jiaruixu/work_space/openboost/src/openboost/data.py", line 56, in __post_init__ + raise ValueError("aligned unique integer row IDs required") +ValueError: aligned unique integer row IDs required diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/1/execution.json b/benchmarks/v1/evidence/classification-048/failed/A2/1/execution.json new file mode 100644 index 0000000..08fb27f --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/1/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/1/job.json" + ], + "exit_code": 1, + "reason": "worker exited nonzero", + "status": "error", + "threads": 1, + "timeout_s": 90, + "wall_s": 0.24909150000894442 +} diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/1/worker.log b/benchmarks/v1/evidence/classification-048/failed/A2/1/worker.log new file mode 100644 index 0000000..0e22e4a --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/1/worker.log @@ -0,0 +1,13 @@ +Traceback (most recent call last): + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 158, in + main() + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 150, in main + prediction, saved, training = fit(job, arrays) + ^^^^^^^^^^^^^^^^ + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 91, in fit + data = NumericData(x, ids, names) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 7, in __init__ + File "/Users/jiaruixu/work_space/openboost/src/openboost/data.py", line 56, in __post_init__ + raise ValueError("aligned unique integer row IDs required") +ValueError: aligned unique integer row IDs required diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/2/execution.json b/benchmarks/v1/evidence/classification-048/failed/A2/2/execution.json new file mode 100644 index 0000000..5fc524a --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/2/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/2/job.json" + ], + "exit_code": 1, + "reason": "worker exited nonzero", + "status": "error", + "threads": 1, + "timeout_s": 90, + "wall_s": 0.2513757090055151 +} diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/2/worker.log b/benchmarks/v1/evidence/classification-048/failed/A2/2/worker.log new file mode 100644 index 0000000..0e22e4a --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/2/worker.log @@ -0,0 +1,13 @@ +Traceback (most recent call last): + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 158, in + main() + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 150, in main + prediction, saved, training = fit(job, arrays) + ^^^^^^^^^^^^^^^^ + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 91, in fit + data = NumericData(x, ids, names) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 7, in __init__ + File "/Users/jiaruixu/work_space/openboost/src/openboost/data.py", line 56, in __post_init__ + raise ValueError("aligned unique integer row IDs required") +ValueError: aligned unique integer row IDs required diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/3/execution.json b/benchmarks/v1/evidence/classification-048/failed/A2/3/execution.json new file mode 100644 index 0000000..3a42871 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/3/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/3/job.json" + ], + "exit_code": 1, + "reason": "worker exited nonzero", + "status": "error", + "threads": 1, + "timeout_s": 90, + "wall_s": 0.2486497500067344 +} diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/3/worker.log b/benchmarks/v1/evidence/classification-048/failed/A2/3/worker.log new file mode 100644 index 0000000..0e22e4a --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/3/worker.log @@ -0,0 +1,13 @@ +Traceback (most recent call last): + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 158, in + main() + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 150, in main + prediction, saved, training = fit(job, arrays) + ^^^^^^^^^^^^^^^^ + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 91, in fit + data = NumericData(x, ids, names) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 7, in __init__ + File "/Users/jiaruixu/work_space/openboost/src/openboost/data.py", line 56, in __post_init__ + raise ValueError("aligned unique integer row IDs required") +ValueError: aligned unique integer row IDs required diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/4/execution.json b/benchmarks/v1/evidence/classification-048/failed/A2/4/execution.json new file mode 100644 index 0000000..d912964 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/4/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/4/job.json" + ], + "exit_code": 1, + "reason": "worker exited nonzero", + "status": "error", + "threads": 1, + "timeout_s": 90, + "wall_s": 0.25176929199369624 +} diff --git a/benchmarks/v1/evidence/classification-048/failed/A2/4/worker.log b/benchmarks/v1/evidence/classification-048/failed/A2/4/worker.log new file mode 100644 index 0000000..0e22e4a --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/A2/4/worker.log @@ -0,0 +1,13 @@ +Traceback (most recent call last): + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 158, in + main() + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 150, in main + prediction, saved, training = fit(job, arrays) + ^^^^^^^^^^^^^^^^ + File "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", line 91, in fit + data = NumericData(x, ids, names) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 7, in __init__ + File "/Users/jiaruixu/work_space/openboost/src/openboost/data.py", line 56, in __post_init__ + raise ValueError("aligned unique integer row IDs required") +ValueError: aligned unique integer row IDs required diff --git a/benchmarks/v1/evidence/classification-048/failed/summary.json b/benchmarks/v1/evidence/classification-048/failed/summary.json new file mode 100644 index 0000000..1a5c8f9 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/summary.json @@ -0,0 +1,371 @@ +{ + "scope": "Current A1/A2/A3/A6/A11 real-data validation plumbing only; four rounds, no test scores, quality or performance claim", + "revision": "b430df0fcd668a5cc524e7be4c9b3ab92003e62f", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "benchmarks.v1.openboost_worker_smoke", + "/private/tmp/openboost-classification-048", + "--applications", + "A2" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "gpu": null, + "threads": 1, + "memory_cap": null, + "packages": { + "numpy": "2.3.5", + "openboost": "1.0.0rc1" + }, + "sources": { + "src/openboost/__init__.py": "b0a76b68a1824abef45bf30d3d094dce7de2ec9deef3d5cbe862a6b3f6038788", + "src/openboost/artifacts.py": "fc5232f29d2b9d55676035bbd5bb9fe726babba5fcdfbd84ff18133de3e9a817", + "src/openboost/binning.py": "e77bdbaf42121ba5dde8ec681371bd0c7d1e31fada19618d796279cc0b322a7d", + "src/openboost/composition.py": "a52dc4d09582031441dbf2c4a3ed889851783a2f4fece50506fa5e0e0501ccec", + "src/openboost/data.py": "93b4eb6487e7fb21c960d19906c7a49b3df9fa28b2a2bacdc8ea6437889f2343", + "src/openboost/leaves.py": "3bff04ff4b1eff1a1d58f6ce5e29f15391e8b2e9a80a696c6fee988d1be95b75", + "src/openboost/multioutput.py": "98c192719bb9ee787ec385a2d219a2f52aacebc161cc9fc1275006f49daad76d", + "src/openboost/objectives.py": "fcf847bda2917354093e03ce42bd22e0fd85e391185d3141318cb7ccecd721f7", + "src/openboost/ops.py": "aa73cc40df7c79203505f91e3b86292dffbb3b9348467be60d8587e3bfd8c6ad", + "src/openboost/outputs.py": "f10f3a68b4513cb93c222cd1e39ca238c686c903daa8c8035c417b5916365115", + "src/openboost/ranking.py": "beddf8fb6eeecbed54acdb55147bc251392a6f9fb7b46304e5d80d1d0ee642b3", + "src/openboost/recipes.py": "4fc1cd9971cde4bad95922c0f4e241d169a8a9fa08730070f02033c3405d265c", + "src/openboost/results.py": "b33bb8c658678bf5319a1e76b9a420112debea4f26361acad166f6f1e641585d", + "src/openboost/runs.py": "5a00eead8d6d4d6ddc99a646a0131f04918c619c5061b647ec4fc3c386459dc4", + "src/openboost/runtime.py": "8962e0204d867a1b700ed15e92b155cd6144927b651a79b8083634a0c315ad2d", + "src/openboost/stats.py": "4edbeba39e727f60213f04b5d3592bc82fb02cb1ebd9603ca9f8cee66b932cc9", + "src/openboost/stopping.py": "2374873a01dba8d7b359cd484337de228ca6c805d6a43b23bcf51598416746a1", + "src/openboost/survival.py": "cb168551a5aa026d6c501f4370c7ddc998d3cc7269ce6f17d93258ceadef89e9", + "src/openboost/tree.py": "60dd8cbc56d78913e868a3788bd08c91e858febc4f4c0534831077df640d7581", + "benchmarks/v1/openboost_worker.py": "6029c86e54e4ea3b35944359c6e2a419650de621554246a11d8b0faaee6e0708", + "benchmarks/v1/openboost_predict.py": "b0c21aca3046eb3ad40f52f4c845dc159872d10261a0afca2d1cd01357354d04", + "benchmarks/v1/openboost_worker_smoke.py": "42461c49d72f23a8a1896d3ec3f09bd6e2352f6b9d513865c1c1e6e7e410c020", + "benchmarks/v1/worker_data.py": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "benchmarks/v1/preprocessing.py": "62561effccd6856bc404339dddbc8c2daa3a1d0b011be59edd8fd3435802bc30", + "benchmarks/v1/process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A2": { + "application": "A2", + "dataset": "adult", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "e04e33fd11559a52e9f899fa332d7291d55569b2e824de8d6ce2858338817092" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "9f45ed87577102e8486a09f1c19bb4067fea4ae8d9aca43283c1cb67707ae832" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "f8e859a818998f2c994b98dedbb0171be1eb0b4c6f54dc6fe305b94d448a53bd" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "2e72125409d9cf1fe27e45dd2b614afb7f9dd02ef17597eb5c6172d00cf8f7cc" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "cb1193a91a859878036a72cc2c1712aa0b5fe24d730519a0f484d6ae166f6278" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "54db181f0f5ab5c7fa90595a308f7584d079c066cc9d1ef4e7118631e27be888" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bee5ad1786121477fd4d262197bc66eee98cefad9eb9662b2f385da02958ce98" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "c57b4786ab885b7da2fd5dc2725adba94c25836501757fce55e8811f45ac489e" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "181f31e71c136cd6335c4077ea29a39dfa47abd3eefed55f4b05db70289cc070" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "6d758560150d299bf28b5b9c0093b742918c17bd6b0f8318af88534249263ee2" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "00bdb2099f2313e3b7655d3ba41f3f5509665e36e6f269e7c74d6ecc89f688b7" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "c5bda1ce25eb633c6d73572b5b8ce5a48b14e0c837fdf6ef2010ad64e2f2cbab" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "035f87ddc1ffda1c5ddd877456ccb79f25c579486f558335467212b1a9c118f8" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "e11c4eb51ff1d7a1fa54924e8cc30d5e562fc775380a2045be8e43317ea42a02" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "f8341f7a2900ac706f37f2eca92745d0a0d771b33fcdfbe61496d2e6ebf52b3d" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "6f4eb8e49ee7879149de5ace3c26ea9c6a79d9f9a59c6c18bfb99bccd5747c72", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/0/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "error", + "exit_code": 1, + "reason": "worker exited nonzero", + "wall_s": 0.257000915997196, + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "application": "A2", + "fold": 0, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 0, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048/A2/0/worker-input.npz", + "classes": 2 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/1/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "error", + "exit_code": 1, + "reason": "worker exited nonzero", + "wall_s": 0.24909150000894442, + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "application": "A2", + "fold": 1, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 1, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048/A2/1/worker-input.npz", + "classes": 2 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/2/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "error", + "exit_code": 1, + "reason": "worker exited nonzero", + "wall_s": 0.2513757090055151, + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "application": "A2", + "fold": 2, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 2, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048/A2/2/worker-input.npz", + "classes": 2 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/3/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "error", + "exit_code": 1, + "reason": "worker exited nonzero", + "wall_s": 0.2486497500067344, + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "application": "A2", + "fold": 3, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 3, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048/A2/3/worker-input.npz", + "classes": 2 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048/A2/4/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "error", + "exit_code": 1, + "reason": "worker exited nonzero", + "wall_s": 0.25176929199369624, + "artifacts": { + "worker.log": "dc6176d63aad70b292eb708d778311f9c3a6992f97e2813cfb6475b69321c45b" + }, + "application": "A2", + "fold": 4, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 4, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048/A2/4/worker-input.npz", + "classes": 2 + } + } + ] +} diff --git a/benchmarks/v1/evidence/classification-048/failed/worker-source.txt b/benchmarks/v1/evidence/classification-048/failed/worker-source.txt new file mode 100644 index 0000000..229d7e3 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/failed/worker-source.txt @@ -0,0 +1,158 @@ +"""Current CPU A1/A2/A3/A6/A11 trials on frozen encoded train/validation packets. + +Explicit validation targets are required even with fixed budgets. The caller +controls process threads and resource limits. Test arrays are always rejected. +""" + +import argparse +import json +from dataclasses import asdict +from pathlib import Path + +import numpy as np + +from openboost import ClassSchema, NumericData, Problem, RunContext +from openboost.multioutput import TargetScale +from openboost.recipes import binary, multi_squared, multiclass, normal, squared + +if __package__: + from benchmarks.v1.openboost_predict import OUTPUTS, predict_saved + from benchmarks.v1.preprocessing import fit_target_scale +else: + from openboost_predict import OUTPUTS, predict_saved + from preprocessing import fit_target_scale + + +def fit(job, arrays): + classification = job.get("application") in {"A2", "A3"} + required = {"application", "library", "device", "seed", "threads", "config"} + if ( + not required <= set(job) + or set(job) + - required + - {"early_stopping_rounds", "input_npz"} + - ({"classes"} if classification else set()) + or job["application"] not in OUTPUTS + or job["library"] != "openboost" + or job["device"] != "cpu" + ): + raise ValueError("unsupported current OpenBoost job") + if type(job["threads"]) is not int or job["threads"] != 1: + raise ValueError("current worker requires one process thread") + if type(job["seed"]) is not int or job["seed"] < 0: + raise ValueError("nonnegative integer seed required") + needed = {"x_train", "y_train", "x_validation", "y_validation", "validation_row_ids"} + if not needed <= set(arrays) or set(arrays) - needed - {"weight_train", "weight_validation"}: + raise ValueError("explicit train/validation arrays only; no test arrays") + cfg = dict(job["config"]) + if cfg.pop("seed_from_fold", True) is not True: + raise ValueError("seed semantics differ") + allowed = {"rounds", "learning_rate", "max_depth", "reg_lambda", "bins"} + if job["application"] == "A11": + allowed |= {"mode", "damping", "minimum_scale"} + if job["application"] == "A6": + allowed |= {"mode"} + if set(cfg) - allowed or not {"rounds", "learning_rate"} <= set(cfg): + raise ValueError("unsupported current recipe config") + if type(cfg["rounds"]) is not int or cfg["rounds"] <= 0: + raise ValueError("positive round budget required") + classes = None + if classification: + count = job.get("classes") + if type(count) is not int or (count != 2 if job["application"] == "A2" else count < 3): + raise ValueError("explicit canonical classification count required") + classes = ClassSchema(tuple(range(count))) + problems = [] + width = 1 if job["application"] == "A1" else 2 + if classification: + width = 1 if job["application"] == "A2" else count + multi = job["application"] == "A6" + target_scale = None + scale = None + names = None + for part in ("train", "validation"): + x, y = np.asarray(arrays["x_" + part]), np.asarray(arrays["y_" + part]) + if ( + x.ndim != 2 + or ( + y.ndim != 2 or not y.shape[1] or len(y) != len(x) if multi else y.shape != (len(x),) + ) + or not len(x) + or not np.isfinite(x).all() + or not np.isfinite(y).all() + ): + raise ValueError("finite encoded inputs and aligned task targets required") + if multi and part == "train": + width = y.shape[1] + target_scale = fit_target_scale(y) + scale = TargetScale(target_scale["mean"], target_scale["std"], target_scale["constant"]) + names = tuple(f"x{i}" for i in range(x.shape[1])) if names is None else names + ids = np.arange(len(x)) if part == "train" else arrays["validation_row_ids"] + data = NumericData(x, ids, names) + problems.append( + Problem( + data, + y if multi else y[:, None], + data.row_ids, + weight=arrays.get("weight_" + part), + raw_width=width, + classes=classes, + ) + ) + if multi: + problems = [scale.transform(p) for p in problems] + recipe = {"A1": squared, "A2": binary, "A3": multiclass, "A6": multi_squared, "A11": normal}[ + job["application"] + ] + result = recipe( + *problems, + context=RunContext("evaluation", job["seed"]), + patience=job.get("early_stopping_rounds"), + **cfg, + ) + selection = "final" if job.get("early_stopping_rounds") is None else "best_validation" + model = result.state.model if selection == "final" else result.state.best_model + saved = dict( + format="openboost-evaluation-v1", + application=job["application"], + output=OUTPUTS[job["application"]], + model=model.record(), + ) + if multi: + saved["target_scale"] = target_scale + prediction = predict_saved(saved, arrays["x_validation"]) + training = dict( + selection=selection, + stop={**asdict(result.stop), "reason": result.stop.reason}, + accepted_commits=result.state.version, + selected_model_identity=model.identity, + best_validation_score=result.state.best_score, + output=saved["output"], + ) + if classification: + training.update(class_order=list(classes.values), selection_metric="logloss") + if multi: + training.update( + target_scale=target_scale, + scale_convention="unweighted_train_population", + selection_metric="row_mean_sum_standardized_half_squared_error", + ) + return prediction, saved, training + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("job", type=Path) + args = parser.parse_args() + job = json.loads(args.job.read_text()) + with np.load(job["input_npz"], allow_pickle=False) as data: + arrays = {name: data[name] for name in data.files} + prediction, saved, training = fit(job, arrays) + # model.bin is UTF-8 JSON, not pickle; the process runner requires this filename. + Path("model.bin").write_text(json.dumps(saved, allow_nan=False) + "\n") + np.savez("predictions.npz", row_ids=arrays["validation_row_ids"], prediction=prediction) + Path("training.json").write_text(json.dumps(training, indent=2, allow_nan=False) + "\n") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/0/execution.json b/benchmarks/v1/evidence/classification-048/passed/A2/0/execution.json new file mode 100644 index 0000000..73f0718 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/0/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "1d000c2c34e06d48b211bcf067e058c2c79deca2a989380a2a01155c4acd7f72", + "predictions.npz": "0b55339b55274573a1e5f5a3392d3c39743f972c538dbd928b2cbfccd9efa763", + "training.json": "f62fd3c2c845a584108e1e6fb52765e36b0c3bc1f957e93999d4471cd601f4fa", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/0/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 4.983045166009106 +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/0/model.bin b/benchmarks/v1/evidence/classification-048/passed/A2/0/model.bin new file mode 100644 index 0000000..080fb39 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/0/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A2", "output": "positive_class_probability", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "base": [-1.1483737982271576], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 61.0, 66.03125], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3432.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.388658543417668e-14], [-0.9540425498028345], [1.1231935151804828], [-1.0446717054695998], [3.937349239352504], [0.4918155091415034], [2.6063015851047706]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 61.0, 66.03125], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3432.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.004318419711083009], [-0.9075463606129377], [0.9516661428422478], [-0.9975032169204724], [2.963332797694123], [0.4311123215492875], [2.061474197631016]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 61.0, 66.03125], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3432.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 4, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.0068631294387180896], [-0.8648864829346248], [0.820701824040363], [-0.9534633579920581], [2.440260771740937], [0.02576102250532801], [1.3849505235408137]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 61.0, 66.03125], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3432.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 2, -1, -1, -1, -1], "threshold": [0, 2, 1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.008648747993949518], [-0.8251960857615269], [0.7144239956904299], [-0.9118697803139116], [2.1087750812249264], [0.520423007853704], [2.39252016889048]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": [0, 1]}} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/0/predictions.npz b/benchmarks/v1/evidence/classification-048/passed/A2/0/predictions.npz new file mode 100644 index 0000000..8d14126 Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/0/predictions.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/0/replay.npz b/benchmarks/v1/evidence/classification-048/passed/A2/0/replay.npz new file mode 100644 index 0000000..8d14126 Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/0/replay.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/0/training.json b/benchmarks/v1/evidence/classification-048/passed/A2/0/training.json new file mode 100644 index 0000000..2e13979 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/0/training.json @@ -0,0 +1,22 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4656077040877246, + "last_score": 0.4656077040877246, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "82ce4e57bc65e3443e40dcf1201fb39f6ddde78c0d29287322f9632cf45440b1", + "best_validation_score": 0.4656077040877246, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/0/worker.log b/benchmarks/v1/evidence/classification-048/passed/A2/0/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/1/execution.json b/benchmarks/v1/evidence/classification-048/passed/A2/1/execution.json new file mode 100644 index 0000000..be38529 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/1/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "5c9ac6e62b90f3a02a31ee49e1e13d29d98e1269a900d482d0e47b464d13cb58", + "predictions.npz": "1313f9399da012ab46ba2288cd4233f4a375756f409f729b16ef8f2d03ba2960", + "training.json": "de6d5ebc2f46a73cbf9fb97e586e334f688357a8d211e4d557b9b62fe220e527", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/1/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 4.582771666988265 +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/1/model.bin b/benchmarks/v1/evidence/classification-048/passed/A2/1/model.bin new file mode 100644 index 0000000..080977b --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/1/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A2", "output": "positive_class_probability", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "base": [-1.1483737982271576], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 55.0, 58.0, 61.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3456.5, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.2835218425119377e-14], [-0.9527754198040692], [1.1132432566655315], [-1.0377210980008305], [3.9668210088287332], [0.4858502640511546], [2.5938300391615043]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 55.0, 58.0, 61.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3456.5, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 6, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.004248939298981032], [-0.9061906600489279], [0.94369727360413], [-0.9904642104269421], [2.9835353985569144], [0.38677384879493554], [1.9858766090130473]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 55.0, 58.0, 61.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3456.5, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 4, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.006784436222629791], [-0.8633719255884952], [0.8138247593832417], [-0.9462993097466148], [2.457404080863465], [0.025881246817799707], [1.3743138157366672]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 55.0, 58.0, 61.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3456.5, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 30.0, 35.0, 37.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 2, -1, -1, -1, -1], "threshold": [0, 2, 1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.008534137666391476], [-0.8234730409912914], [0.7086213545097472], [-0.9045559660201679], [2.124886985186068], [0.510568324262186], [2.3970470369474177]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": [0, 1]}} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/1/predictions.npz b/benchmarks/v1/evidence/classification-048/passed/A2/1/predictions.npz new file mode 100644 index 0000000..d5d084e Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/1/predictions.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/1/replay.npz b/benchmarks/v1/evidence/classification-048/passed/A2/1/replay.npz new file mode 100644 index 0000000..d5d084e Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/1/replay.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/1/training.json b/benchmarks/v1/evidence/classification-048/passed/A2/1/training.json new file mode 100644 index 0000000..3fe5ecf --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/1/training.json @@ -0,0 +1,22 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4623905852192714, + "last_score": 0.4623905852192714, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "0cfecd85ea1c6290095fbb9f97f681c6f4da8aa2d711773f1d2624b3cbee34d6", + "best_validation_score": 0.4623905852192714, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/1/worker.log b/benchmarks/v1/evidence/classification-048/passed/A2/1/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/2/execution.json b/benchmarks/v1/evidence/classification-048/passed/A2/2/execution.json new file mode 100644 index 0000000..4422478 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/2/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "af869caacf551903546fec33243dd5c0c2f35122926442b650f8fc9b37465465", + "predictions.npz": "07a6ccf24c6233ba7bb8380dba7825df8b9e0e17efdc13c6a37a3275044548ab", + "training.json": "db568b8daed5ede594be8db676f5dc5f82f39560ed7fb13e2d27da8cc4eb0f7e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/2/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 4.735486207995564 +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/2/model.bin b/benchmarks/v1/evidence/classification-048/passed/A2/2/model.bin new file mode 100644 index 0000000..10a1bc8 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/2/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A2", "output": "positive_class_probability", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "base": [-1.148373798227158], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 50.0, 51.0, 53.0, 56.0, 59.0, 62.0, 67.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.2019612361666703e-14], [-0.9558554476674211], [1.1244587572762672], [-1.0405633552868943], [3.944847505169965], [0.4840340210852353], [2.6339492565722806]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 50.0, 51.0, 53.0, 56.0, 59.0, 62.0, 67.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 4, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.004322014890858551], [-0.9092706979442317], [0.9526380401754363], [-0.9933418815614902], [2.9693955172647812], [0.047371258733305006], [1.6211543454427397]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 50.0, 51.0, 53.0, 56.0, 59.0, 62.0, 67.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 6, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.007018434596376032], [-0.8664624495540834], [0.8200055507880064], [-0.9492272308867249], [2.445780764734275], [0.3135214238546051], [1.7348237153652053]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 50.0, 51.0, 53.0, 56.0, 59.0, 62.0, 67.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 2, -1, -1, -1, -1], "threshold": [0, 2, 1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.008661797923699043], [-0.826580965029604], [0.7149853947997257], [-0.9075441013381054], [2.1141447750815128], [0.5125857876696551], [2.4468593387280864]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": [0, 1]}} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/2/predictions.npz b/benchmarks/v1/evidence/classification-048/passed/A2/2/predictions.npz new file mode 100644 index 0000000..2d485ff Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/2/predictions.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/2/replay.npz b/benchmarks/v1/evidence/classification-048/passed/A2/2/replay.npz new file mode 100644 index 0000000..2d485ff Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/2/replay.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/2/training.json b/benchmarks/v1/evidence/classification-048/passed/A2/2/training.json new file mode 100644 index 0000000..4dd27de --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/2/training.json @@ -0,0 +1,22 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.46506133562491814, + "last_score": 0.46506133562491814, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "e66e09e89580f83072ca05d70ed9d249af598946e7ccc2f7a9ce85d5652b9151", + "best_validation_score": 0.46506133562491814, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/2/worker.log b/benchmarks/v1/evidence/classification-048/passed/A2/2/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/3/execution.json b/benchmarks/v1/evidence/classification-048/passed/A2/3/execution.json new file mode 100644 index 0000000..ed57df0 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/3/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "b55710a82dce488d0cb850ebec8a94827514dfbda666ab4bf69915e692fb80b6", + "predictions.npz": "e97fd0cf4ad336a213e011bc692204deb35884ce92151d59e2081ae0567680eb", + "training.json": "6df1a6dad14d77b2af9969a93866d7201fbf511e824e776ef308de63abb374f4", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/3/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 4.678132083994569 +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/3/model.bin b/benchmarks/v1/evidence/classification-048/passed/A2/3/model.bin new file mode 100644 index 0000000..d5690ba --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/3/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A2", "output": "positive_class_probability", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "base": [-1.148373798227158], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3411.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 23.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.2573769466655711e-14], [-0.9608446000647293], [1.1326008703696973], [-1.046663335831165], [3.9258784930042685], [0.5052720820512459], [2.6118430940745303]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3411.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 23.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 4, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.004318299497913468], [-0.9142909774387863], [0.9592159821855966], [-0.999521214501695], [2.956931805020618], [0.06705778050348529], [1.615424774861903]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3411.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 23.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.007027845270041498], [-0.8715521914616886], [0.825405427109679], [-0.9555184489385014], [2.435437958733089], [0.3633574551721405], [1.7851528794379583]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3411.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 23.0, 28.0, 30.0, 35.0, 36.0, 40.0, 43.0, 45.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 2, -1, -1, -1, -1], "threshold": [0, 2, 1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.008666074531622407], [-0.8317665713037776], [0.7195702898795887], [-0.9139692720642179], [2.1045465585995773], [0.5197338285577295], [2.4274495952434756]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": [0, 1]}} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/3/predictions.npz b/benchmarks/v1/evidence/classification-048/passed/A2/3/predictions.npz new file mode 100644 index 0000000..98131ac Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/3/predictions.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/3/replay.npz b/benchmarks/v1/evidence/classification-048/passed/A2/3/replay.npz new file mode 100644 index 0000000..98131ac Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/3/replay.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/3/training.json b/benchmarks/v1/evidence/classification-048/passed/A2/3/training.json new file mode 100644 index 0000000..17c1ad2 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/3/training.json @@ -0,0 +1,22 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4657678402367944, + "last_score": 0.4657678402367944, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "0f71605492652acbb37b97b377dd9a839bb8389ea972972a364ff650e739c0d7", + "best_validation_score": 0.4657678402367944, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/3/worker.log b/benchmarks/v1/evidence/classification-048/passed/A2/3/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/4/execution.json b/benchmarks/v1/evidence/classification-048/passed/A2/4/execution.json new file mode 100644 index 0000000..36efed9 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/4/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "09ab948564a487b6799b16be085a8d97ff5fbf1d7e9b0f69340f5aa7ca20c8e5", + "predictions.npz": "8b2bbaca6593b544cdd3c6fc9c364a7ecf5c2ae53bd83c2563fa63d27309629a", + "training.json": "2b6d510d0d5869c2e5f1c1fb74e1f32746d8c2aa73975945354dc6e3f9adea53", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/4/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 4.623000916995807 +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/4/model.bin b/benchmarks/v1/evidence/classification-048/passed/A2/4/model.bin new file mode 100644 index 0000000..1c2bffd --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/4/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A2", "output": "positive_class_probability", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "base": [-1.148373798227158], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.1079156028819289e-14], [-0.9562518584756571], [1.119723652774749], [-1.0452681674899549], [3.933077210935888], [0.47711615488154296], [2.624720667530181]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 6, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00434226448164964], [-0.9097352357982064], [0.9488395309601728], [-0.9981075780219194], [2.9609503125493775], [0.37687343460796996], [2.0047173928279203]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 1, -1, -1, -1, -1], "threshold": [0, 2, 4, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.006925696690944172], [-0.8670473544745879], [0.8181865640526819], [-0.9540788200059876], [2.438465583017687], [0.025987398505936052], [1.3822025530279687]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38", "x39", "x40", "x41", "x42", "x43", "x44", "x45", "x46", "x47", "x48", "x49", "x50", "x51", "x52", "x53", "x54", "x55", "x56", "x57", "x58", "x59", "x60", "x61", "x62", "x63", "x64", "x65", "x66", "x67", "x68", "x69", "x70", "x71", "x72", "x73", "x74", "x75", "x76", "x77", "x78", "x79", "x80", "x81", "x82", "x83", "x84", "x85", "x86", "x87", "x88", "x89", "x90", "x91", "x92", "x93", "x94", "x95", "x96", "x97", "x98", "x99", "x100", "x101", "x102", "x103", "x104", "x105", "x106", "x107", "x108", "x109", "x110", "x111", "x112", "x113", "x114", "x115", "x116"], "cuts": [[19.0, 20.0, 21.0, 23.0, 24.0, 25.0, 26.0, 28.0, 29.0, 30.0, 31.0, 32.0, 34.0, 35.0, 36.0, 37.0, 38.0, 40.0, 41.0, 42.0, 43.0, 45.0, 46.0, 48.0, 49.0, 51.0, 53.0, 56.0, 58.0, 62.0, 66.0], [4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [0.0, 3464.0, 7688.0], [0.0, 1741.0], [15.0, 20.0, 24.0, 28.0, 30.0, 35.0, 36.0, 40.0, 44.0, 45.0, 46.0, 50.0, 55.0, 60.0, 65.0], [], [], [], [], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [], [0.0], [0.0], [], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0]], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [29, 2, 2, -1, -1, -1, -1], "threshold": [0, 2, 1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.008695487231039129], [-0.8273232381236892], [0.7124148194754071], [-0.9124985222183162], [2.107200701631204], [0.5128942745306813], [2.452147872681472]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": [0, 1]}} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/4/predictions.npz b/benchmarks/v1/evidence/classification-048/passed/A2/4/predictions.npz new file mode 100644 index 0000000..d6c7203 Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/4/predictions.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/4/replay.npz b/benchmarks/v1/evidence/classification-048/passed/A2/4/replay.npz new file mode 100644 index 0000000..d6c7203 Binary files /dev/null and b/benchmarks/v1/evidence/classification-048/passed/A2/4/replay.npz differ diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/4/training.json b/benchmarks/v1/evidence/classification-048/passed/A2/4/training.json new file mode 100644 index 0000000..256d794 --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/A2/4/training.json @@ -0,0 +1,22 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4660557097861674, + "last_score": 0.4660557097861674, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "86454694cb1d995c18cf3cbdf34c37ad733d81b378c15473a1215e12cee3cc13", + "best_validation_score": 0.4660557097861674, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" +} diff --git a/benchmarks/v1/evidence/classification-048/passed/A2/4/worker.log b/benchmarks/v1/evidence/classification-048/passed/A2/4/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/classification-048/passed/summary.json b/benchmarks/v1/evidence/classification-048/passed/summary.json new file mode 100644 index 0000000..b02ed1f --- /dev/null +++ b/benchmarks/v1/evidence/classification-048/passed/summary.json @@ -0,0 +1,556 @@ +{ + "scope": "Current A1/A2/A3/A6/A11 real-data validation plumbing only; four rounds, no test scores, quality or performance claim", + "revision": "b430df0fcd668a5cc524e7be4c9b3ab92003e62f", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "benchmarks.v1.openboost_worker_smoke", + "/private/tmp/openboost-classification-048-fixed", + "--applications", + "A2" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "gpu": null, + "threads": 1, + "memory_cap": null, + "packages": { + "numpy": "2.3.5", + "openboost": "1.0.0rc1" + }, + "sources": { + "src/openboost/__init__.py": "b0a76b68a1824abef45bf30d3d094dce7de2ec9deef3d5cbe862a6b3f6038788", + "src/openboost/artifacts.py": "fc5232f29d2b9d55676035bbd5bb9fe726babba5fcdfbd84ff18133de3e9a817", + "src/openboost/binning.py": "e77bdbaf42121ba5dde8ec681371bd0c7d1e31fada19618d796279cc0b322a7d", + "src/openboost/composition.py": "a52dc4d09582031441dbf2c4a3ed889851783a2f4fece50506fa5e0e0501ccec", + "src/openboost/data.py": "93b4eb6487e7fb21c960d19906c7a49b3df9fa28b2a2bacdc8ea6437889f2343", + "src/openboost/leaves.py": "3bff04ff4b1eff1a1d58f6ce5e29f15391e8b2e9a80a696c6fee988d1be95b75", + "src/openboost/multioutput.py": "98c192719bb9ee787ec385a2d219a2f52aacebc161cc9fc1275006f49daad76d", + "src/openboost/objectives.py": "fcf847bda2917354093e03ce42bd22e0fd85e391185d3141318cb7ccecd721f7", + "src/openboost/ops.py": "aa73cc40df7c79203505f91e3b86292dffbb3b9348467be60d8587e3bfd8c6ad", + "src/openboost/outputs.py": "f10f3a68b4513cb93c222cd1e39ca238c686c903daa8c8035c417b5916365115", + "src/openboost/ranking.py": "beddf8fb6eeecbed54acdb55147bc251392a6f9fb7b46304e5d80d1d0ee642b3", + "src/openboost/recipes.py": "4fc1cd9971cde4bad95922c0f4e241d169a8a9fa08730070f02033c3405d265c", + "src/openboost/results.py": "b33bb8c658678bf5319a1e76b9a420112debea4f26361acad166f6f1e641585d", + "src/openboost/runs.py": "5a00eead8d6d4d6ddc99a646a0131f04918c619c5061b647ec4fc3c386459dc4", + "src/openboost/runtime.py": "8962e0204d867a1b700ed15e92b155cd6144927b651a79b8083634a0c315ad2d", + "src/openboost/stats.py": "4edbeba39e727f60213f04b5d3592bc82fb02cb1ebd9603ca9f8cee66b932cc9", + "src/openboost/stopping.py": "2374873a01dba8d7b359cd484337de228ca6c805d6a43b23bcf51598416746a1", + "src/openboost/survival.py": "cb168551a5aa026d6c501f4370c7ddc998d3cc7269ce6f17d93258ceadef89e9", + "src/openboost/tree.py": "60dd8cbc56d78913e868a3788bd08c91e858febc4f4c0534831077df640d7581", + "benchmarks/v1/openboost_worker.py": "d53f4796353430002ca2231de1852c139492fd7de1c7e4936374da1befebd33d", + "benchmarks/v1/openboost_predict.py": "b0c21aca3046eb3ad40f52f4c845dc159872d10261a0afca2d1cd01357354d04", + "benchmarks/v1/openboost_worker_smoke.py": "42461c49d72f23a8a1896d3ec3f09bd6e2352f6b9d513865c1c1e6e7e410c020", + "benchmarks/v1/worker_data.py": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "benchmarks/v1/preprocessing.py": "62561effccd6856bc404339dddbc8c2daa3a1d0b011be59edd8fd3435802bc30", + "benchmarks/v1/process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A2": { + "application": "A2", + "dataset": "adult", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "e04e33fd11559a52e9f899fa332d7291d55569b2e824de8d6ce2858338817092" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "9f45ed87577102e8486a09f1c19bb4067fea4ae8d9aca43283c1cb67707ae832" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "f8e859a818998f2c994b98dedbb0171be1eb0b4c6f54dc6fe305b94d448a53bd" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "2e72125409d9cf1fe27e45dd2b614afb7f9dd02ef17597eb5c6172d00cf8f7cc" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "cb1193a91a859878036a72cc2c1712aa0b5fe24d730519a0f484d6ae166f6278" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "54db181f0f5ab5c7fa90595a308f7584d079c066cc9d1ef4e7118631e27be888" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bee5ad1786121477fd4d262197bc66eee98cefad9eb9662b2f385da02958ce98" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "c57b4786ab885b7da2fd5dc2725adba94c25836501757fce55e8811f45ac489e" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "181f31e71c136cd6335c4077ea29a39dfa47abd3eefed55f4b05db70289cc070" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "6d758560150d299bf28b5b9c0093b742918c17bd6b0f8318af88534249263ee2" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "00bdb2099f2313e3b7655d3ba41f3f5509665e36e6f269e7c74d6ecc89f688b7" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "c5bda1ce25eb633c6d73572b5b8ce5a48b14e0c837fdf6ef2010ad64e2f2cbab" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "035f87ddc1ffda1c5ddd877456ccb79f25c579486f558335467212b1a9c118f8" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "e11c4eb51ff1d7a1fa54924e8cc30d5e562fc775380a2045be8e43317ea42a02" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "f8341f7a2900ac706f37f2eca92745d0a0d771b33fcdfbe61496d2e6ebf52b3d" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "6f4eb8e49ee7879149de5ace3c26ea9c6a79d9f9a59c6c18bfb99bccd5747c72", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/0/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 4.983045166009106, + "artifacts": { + "model.bin": "1d000c2c34e06d48b211bcf067e058c2c79deca2a989380a2a01155c4acd7f72", + "training.json": "f62fd3c2c845a584108e1e6fb52765e36b0c3bc1f957e93999d4471cd601f4fa", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0b55339b55274573a1e5f5a3392d3c39743f972c538dbd928b2cbfccd9efa763" + }, + "application": "A2", + "fold": 0, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 0, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048-fixed/A2/0/worker-input.npz", + "classes": 2 + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-classification-048-fixed/A2/0/fit/model.bin", + "/private/tmp/openboost-classification-048-fixed/A2/0/fit/features.npz", + "/private/tmp/openboost-classification-048-fixed/A2/0/fit/replay.npz" + ], + "prediction_shape": [ + 6513 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4656077040877246, + "last_score": 0.4656077040877246, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "82ce4e57bc65e3443e40dcf1201fb39f6ddde78c0d29287322f9632cf45440b1", + "best_validation_score": 0.4656077040877246, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" + }, + "replay_sha256": "0b55339b55274573a1e5f5a3392d3c39743f972c538dbd928b2cbfccd9efa763" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/1/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 4.582771666988265, + "artifacts": { + "model.bin": "5c9ac6e62b90f3a02a31ee49e1e13d29d98e1269a900d482d0e47b464d13cb58", + "training.json": "de6d5ebc2f46a73cbf9fb97e586e334f688357a8d211e4d557b9b62fe220e527", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1313f9399da012ab46ba2288cd4233f4a375756f409f729b16ef8f2d03ba2960" + }, + "application": "A2", + "fold": 1, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 1, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048-fixed/A2/1/worker-input.npz", + "classes": 2 + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-classification-048-fixed/A2/1/fit/model.bin", + "/private/tmp/openboost-classification-048-fixed/A2/1/fit/features.npz", + "/private/tmp/openboost-classification-048-fixed/A2/1/fit/replay.npz" + ], + "prediction_shape": [ + 6513 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4623905852192714, + "last_score": 0.4623905852192714, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "0cfecd85ea1c6290095fbb9f97f681c6f4da8aa2d711773f1d2624b3cbee34d6", + "best_validation_score": 0.4623905852192714, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" + }, + "replay_sha256": "1313f9399da012ab46ba2288cd4233f4a375756f409f729b16ef8f2d03ba2960" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/2/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 4.735486207995564, + "artifacts": { + "model.bin": "af869caacf551903546fec33243dd5c0c2f35122926442b650f8fc9b37465465", + "training.json": "db568b8daed5ede594be8db676f5dc5f82f39560ed7fb13e2d27da8cc4eb0f7e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "07a6ccf24c6233ba7bb8380dba7825df8b9e0e17efdc13c6a37a3275044548ab" + }, + "application": "A2", + "fold": 2, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 2, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048-fixed/A2/2/worker-input.npz", + "classes": 2 + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-classification-048-fixed/A2/2/fit/model.bin", + "/private/tmp/openboost-classification-048-fixed/A2/2/fit/features.npz", + "/private/tmp/openboost-classification-048-fixed/A2/2/fit/replay.npz" + ], + "prediction_shape": [ + 6513 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.46506133562491814, + "last_score": 0.46506133562491814, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "e66e09e89580f83072ca05d70ed9d249af598946e7ccc2f7a9ce85d5652b9151", + "best_validation_score": 0.46506133562491814, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" + }, + "replay_sha256": "07a6ccf24c6233ba7bb8380dba7825df8b9e0e17efdc13c6a37a3275044548ab" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/3/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 4.678132083994569, + "artifacts": { + "model.bin": "b55710a82dce488d0cb850ebec8a94827514dfbda666ab4bf69915e692fb80b6", + "training.json": "6df1a6dad14d77b2af9969a93866d7201fbf511e824e776ef308de63abb374f4", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "e97fd0cf4ad336a213e011bc692204deb35884ce92151d59e2081ae0567680eb" + }, + "application": "A2", + "fold": 3, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 3, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048-fixed/A2/3/worker-input.npz", + "classes": 2 + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-classification-048-fixed/A2/3/fit/model.bin", + "/private/tmp/openboost-classification-048-fixed/A2/3/fit/features.npz", + "/private/tmp/openboost-classification-048-fixed/A2/3/fit/replay.npz" + ], + "prediction_shape": [ + 6513 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4657678402367944, + "last_score": 0.4657678402367944, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "0f71605492652acbb37b97b377dd9a839bb8389ea972972a364ff650e739c0d7", + "best_validation_score": 0.4657678402367944, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" + }, + "replay_sha256": "e97fd0cf4ad336a213e011bc692204deb35884ce92151d59e2081ae0567680eb" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-classification-048-fixed/A2/4/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 4.623000916995807, + "artifacts": { + "model.bin": "09ab948564a487b6799b16be085a8d97ff5fbf1d7e9b0f69340f5aa7ca20c8e5", + "training.json": "2b6d510d0d5869c2e5f1c1fb74e1f32746d8c2aa73975945354dc6e3f9adea53", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "8b2bbaca6593b544cdd3c6fc9c364a7ecf5c2ae53bd83c2563fa63d27309629a" + }, + "application": "A2", + "fold": 4, + "job": { + "application": "A2", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 4, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-classification-048-fixed/A2/4/worker-input.npz", + "classes": 2 + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-classification-048-fixed/A2/4/fit/model.bin", + "/private/tmp/openboost-classification-048-fixed/A2/4/fit/features.npz", + "/private/tmp/openboost-classification-048-fixed/A2/4/fit/replay.npz" + ], + "prediction_shape": [ + 6513 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4660557097861674, + "last_score": 0.4660557097861674, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "86454694cb1d995c18cf3cbdf34c37ad733d81b378c15473a1215e12cee3cc13", + "best_validation_score": 0.4660557097861674, + "output": "positive_class_probability", + "class_order": [ + 0, + 1 + ], + "selection_metric": "logloss" + }, + "replay_sha256": "8b2bbaca6593b544cdd3c6fc9c364a7ecf5c2ae53bd83c2563fa63d27309629a" + } + ] +} diff --git a/benchmarks/v1/evidence/covertype-049/A3/0/execution.json b/benchmarks/v1/evidence/covertype-049/A3/0/execution.json new file mode 100644 index 0000000..731531e --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/A3/0/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/0/job.json" + ], + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "status": "timeout", + "threads": 1, + "timeout_s": 90, + "wall_s": 90.12871720900876 +} diff --git a/benchmarks/v1/evidence/covertype-049/A3/0/worker.log b/benchmarks/v1/evidence/covertype-049/A3/0/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/covertype-049/A3/1/execution.json b/benchmarks/v1/evidence/covertype-049/A3/1/execution.json new file mode 100644 index 0000000..918e104 --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/A3/1/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/1/job.json" + ], + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "status": "timeout", + "threads": 1, + "timeout_s": 90, + "wall_s": 90.12180937499215 +} diff --git a/benchmarks/v1/evidence/covertype-049/A3/1/worker.log b/benchmarks/v1/evidence/covertype-049/A3/1/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/covertype-049/A3/2/execution.json b/benchmarks/v1/evidence/covertype-049/A3/2/execution.json new file mode 100644 index 0000000..aae4568 --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/A3/2/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/2/job.json" + ], + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "status": "timeout", + "threads": 1, + "timeout_s": 90, + "wall_s": 90.088346250006 +} diff --git a/benchmarks/v1/evidence/covertype-049/A3/2/worker.log b/benchmarks/v1/evidence/covertype-049/A3/2/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/covertype-049/A3/3/execution.json b/benchmarks/v1/evidence/covertype-049/A3/3/execution.json new file mode 100644 index 0000000..f679370 --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/A3/3/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/3/job.json" + ], + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "status": "timeout", + "threads": 1, + "timeout_s": 90, + "wall_s": 90.08695199999784 +} diff --git a/benchmarks/v1/evidence/covertype-049/A3/3/worker.log b/benchmarks/v1/evidence/covertype-049/A3/3/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/covertype-049/A3/4/execution.json b/benchmarks/v1/evidence/covertype-049/A3/4/execution.json new file mode 100644 index 0000000..82962bc --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/A3/4/execution.json @@ -0,0 +1,16 @@ +{ + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/4/job.json" + ], + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "status": "timeout", + "threads": 1, + "timeout_s": 90, + "wall_s": 90.09158687500167 +} diff --git a/benchmarks/v1/evidence/covertype-049/A3/4/worker.log b/benchmarks/v1/evidence/covertype-049/A3/4/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/covertype-049/README.md b/benchmarks/v1/evidence/covertype-049/README.md new file mode 100644 index 0000000..9c22027 --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/README.md @@ -0,0 +1,21 @@ +# Full Covertype worker timeouts + +All five frozen folds timed out at 90 seconds. No models or predictions exist; +probability and fresh-inference checks did not execute. This is failed validation +integration evidence, not a passing A3 result or a comparative speed claim. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-covertype-049 --applications A3 +``` + +Requires pinned Covertype source and preprocessing freeze. Four rounds, depth +two, 32 bins, seven classes, one CPU thread. The summary retains source/revision, +data/packet/fold identities, exact jobs and commands, environment and all five +execution outcomes. Each fold retains its execution record and empty worker log. +Large generated packets are not copied; their frozen hashes and export procedure +are retained. No test labels were scored. CPU source hashes match the recorded +revision; the dirty state consists of planning records created before the run. + +No memory cap or CUDA. Timeouts alone do not identify a hotspot or prove a +mathematical failure. Next bounded phase/stack profiling should use the same +full input before changing code or budgets. diff --git a/benchmarks/v1/evidence/covertype-049/summary.json b/benchmarks/v1/evidence/covertype-049/summary.json new file mode 100644 index 0000000..54680af --- /dev/null +++ b/benchmarks/v1/evidence/covertype-049/summary.json @@ -0,0 +1,371 @@ +{ + "scope": "Current A1/A2/A3/A6/A11 real-data validation plumbing only; four rounds, no test scores, quality or performance claim", + "revision": "0def101a0f91e52a654aa4d3d734144d0a98411f", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "benchmarks.v1.openboost_worker_smoke", + "/private/tmp/openboost-covertype-049", + "--applications", + "A3" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "gpu": null, + "threads": 1, + "memory_cap": null, + "packages": { + "numpy": "2.3.5", + "openboost": "1.0.0rc1" + }, + "sources": { + "src/openboost/__init__.py": "b0a76b68a1824abef45bf30d3d094dce7de2ec9deef3d5cbe862a6b3f6038788", + "src/openboost/artifacts.py": "fc5232f29d2b9d55676035bbd5bb9fe726babba5fcdfbd84ff18133de3e9a817", + "src/openboost/binning.py": "e77bdbaf42121ba5dde8ec681371bd0c7d1e31fada19618d796279cc0b322a7d", + "src/openboost/composition.py": "a52dc4d09582031441dbf2c4a3ed889851783a2f4fece50506fa5e0e0501ccec", + "src/openboost/data.py": "93b4eb6487e7fb21c960d19906c7a49b3df9fa28b2a2bacdc8ea6437889f2343", + "src/openboost/leaves.py": "3bff04ff4b1eff1a1d58f6ce5e29f15391e8b2e9a80a696c6fee988d1be95b75", + "src/openboost/multioutput.py": "98c192719bb9ee787ec385a2d219a2f52aacebc161cc9fc1275006f49daad76d", + "src/openboost/objectives.py": "fcf847bda2917354093e03ce42bd22e0fd85e391185d3141318cb7ccecd721f7", + "src/openboost/ops.py": "aa73cc40df7c79203505f91e3b86292dffbb3b9348467be60d8587e3bfd8c6ad", + "src/openboost/outputs.py": "f10f3a68b4513cb93c222cd1e39ca238c686c903daa8c8035c417b5916365115", + "src/openboost/ranking.py": "beddf8fb6eeecbed54acdb55147bc251392a6f9fb7b46304e5d80d1d0ee642b3", + "src/openboost/recipes.py": "4fc1cd9971cde4bad95922c0f4e241d169a8a9fa08730070f02033c3405d265c", + "src/openboost/results.py": "b33bb8c658678bf5319a1e76b9a420112debea4f26361acad166f6f1e641585d", + "src/openboost/runs.py": "5a00eead8d6d4d6ddc99a646a0131f04918c619c5061b647ec4fc3c386459dc4", + "src/openboost/runtime.py": "8962e0204d867a1b700ed15e92b155cd6144927b651a79b8083634a0c315ad2d", + "src/openboost/stats.py": "4edbeba39e727f60213f04b5d3592bc82fb02cb1ebd9603ca9f8cee66b932cc9", + "src/openboost/stopping.py": "2374873a01dba8d7b359cd484337de228ca6c805d6a43b23bcf51598416746a1", + "src/openboost/survival.py": "cb168551a5aa026d6c501f4370c7ddc998d3cc7269ce6f17d93258ceadef89e9", + "src/openboost/tree.py": "60dd8cbc56d78913e868a3788bd08c91e858febc4f4c0534831077df640d7581", + "benchmarks/v1/openboost_worker.py": "d53f4796353430002ca2231de1852c139492fd7de1c7e4936374da1befebd33d", + "benchmarks/v1/openboost_predict.py": "b0c21aca3046eb3ad40f52f4c845dc159872d10261a0afca2d1cd01357354d04", + "benchmarks/v1/openboost_worker_smoke.py": "42461c49d72f23a8a1896d3ec3f09bd6e2352f6b9d513865c1c1e6e7e410c020", + "benchmarks/v1/worker_data.py": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "benchmarks/v1/preprocessing.py": "62561effccd6856bc404339dddbc8c2daa3a1d0b011be59edd8fd3435802bc30", + "benchmarks/v1/process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A3": { + "application": "A3", + "dataset": "covertype", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "9bc25256b6a57efa51f13e714c6b47496239858ac851e97c3d74ebe47db71f1a" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "be5d90d83aa4daede0678e41425ff4ba5c45dbf074938113af0ad17b421a2f6c" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "36dda823586db6a9ddf91fd0d9e2520339d2673c05376469b685b6477f9a766d" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "0b1dff90f6c0f819af4212b7c8f70f4ad81fc601c82d7cfa654cca1a6818f314" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "939fae1e6bb73bd032846d1d746ca878d9d04e632fa44fa167186f62274ce168" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "d64c60a6f43d571eeddc9a9bcbda756b80fe4941f86322908f32e7f4717fad39" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "4a76893b287be4ae6d426237a31964d3ced678a3146b130774e5375925eb23d1" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "28c6c8c65d8a2bea7ae4143cad6779663210cc9f96fb80c7964916df0f1be0d7" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "58cb3d94f756b63b04a367e966f35cc96f89575a7648a8ddfdc8a8557e91e0d8" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "8bca09f723c78f2d5b76b502a9b9c13c32d128d500468eb569ec3e1206ef8d06" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "5714e839b4f64cb5c9aba8bb348126f0f472adffba9370ca4c2c478e8f922766" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "dc1172c948a9a36e8655ae0cd8abfe06a20e36edb95a2dcf848a0fbca56e05b6" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "0008f26455bff9cf865f311e8e12827f4f7ee3a6e130ae8e7cbddf52bff08e22" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "55d1bfe08be7912d5bf6abc393debce5e3256b966fd028b9c58ff25ae105425d" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "93fbfff4e8b6f0d0af1e2545a2880912000bc10b13edf7ca04558405239c5efa" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3925324d0008087680b73df1ce383154ccb06c15b3781c6d614e461b58caa1a4" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "b9bd2397c5589902abd54b030e034a6d15391d08e3ba7b8417b730d255ebb287" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "8dc679559db25ab5b539ea1b497b17f648b873d53918a55908c6510df3ec9728" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "f30c6b3b8b37878446033c7876190aff6bd7338ee53471965776c3f121ab4062" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "a9cc4943bbe5511863e1f44c50e0a6fa842f255acc4ad641553ac63f9efb177b" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "2887c6bba88c012857b33c316dd0f60bf53218d0d966ab1a1a7e1ea1a7fd54df" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "375c27849c96df36923c5d0725e339d1158147330b7b90f88d9f060c6e7152a0" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "ec378b293641f6c2994840ebeb39545d4b13cafff26e4fe3652ac514a01d83a3" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "c8e80caf8655a4cb2cb2f6a259fba3db8f87279c0992514107465f59b5babd69" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "7113772cb35562a039be77a3874cc6f807886f16f51603c2318547170c2f1153" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "a4c9a1614b7e0d4d74d99194f498a0a387f9d547992981b8c3219ca79cd1f675", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/0/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "timeout", + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "wall_s": 90.12871720900876, + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "application": "A3", + "fold": 0, + "job": { + "application": "A3", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 0, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-covertype-049/A3/0/worker-input.npz", + "classes": 7 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/1/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "timeout", + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "wall_s": 90.12180937499215, + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "application": "A3", + "fold": 1, + "job": { + "application": "A3", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 1, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-covertype-049/A3/1/worker-input.npz", + "classes": 7 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/2/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "timeout", + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "wall_s": 90.088346250006, + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "application": "A3", + "fold": 2, + "job": { + "application": "A3", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 2, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-covertype-049/A3/2/worker-input.npz", + "classes": 7 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/3/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "timeout", + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "wall_s": 90.08695199999784, + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "application": "A3", + "fold": 3, + "job": { + "application": "A3", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 3, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-covertype-049/A3/3/worker-input.npz", + "classes": 7 + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-covertype-049/A3/4/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "timeout", + "exit_code": -9, + "reason": "wall budget exceeded; process group killed", + "wall_s": 90.09158687500167, + "artifacts": { + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "application": "A3", + "fold": 4, + "job": { + "application": "A3", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 4, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-covertype-049/A3/4/worker-input.npz", + "classes": 7 + } + } + ] +} diff --git a/benchmarks/v1/evidence/cpu-capabilities-initial-source.txt b/benchmarks/v1/evidence/cpu-capabilities-initial-source.txt new file mode 100644 index 0000000..1104d3c --- /dev/null +++ b/benchmarks/v1/evidence/cpu-capabilities-initial-source.txt @@ -0,0 +1,265 @@ +"""Installed baseline capability probes: tiny weighted fit, prediction, and reload. + +These probes are not real-data quality results or performance measurements. +Run separately on CPU and a real CUDA host; failures remain in the returned record. +""" + +import argparse +import importlib.metadata +import json +import os +import platform +import subprocess +import tempfile +import time +from pathlib import Path + +import numpy as np + + +def run(device="cpu"): + import catboost as cb + import lightgbm as lgb + import xgboost as xgb + + rng = np.random.default_rng(41) + x = rng.normal(size=(96, 5)) + y = 2 + x[:, 0] + 0.1 * rng.normal(size=96) + weights = np.where(x[:, 1] > 0, 3.0, 0.5) + binary = (x[:, 0] > 0).astype(int) + multi = np.arange(96) % 3 + positive = np.exp(y / 2) + count = rng.poisson(positive) + rounds = 4 + cells = [] + for library in ["xgboost", "lightgbm", "catboost"]: + for task in ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11"]: + start = time.perf_counter() + record = {"library": library, "application": task, "device": device, "status": "error"} + try: + if ( + (library == "lightgbm" and task in ["A10", "A11"]) + or (library == "catboost" and task == "A8") + or (library == "xgboost" and task == "A11") + ): + record.update( + status="unsupported", + reason="no matching builtin; separate outer-loop control required", + ) + continue + target = { + "A2": binary, + "A3": multi, + "A4": multi, + "A6": np.column_stack([y, 2 * y]), + "A7": count, + "A8": positive, + "A9": positive, + "A10": positive, + }.get(task, y) + with tempfile.TemporaryDirectory() as temp: + model_path = Path(temp) / "model.json" + if library == "xgboost": + objectives = { + "A1": "reg:squarederror", + "A2": "binary:logistic", + "A3": "multi:softprob", + "A4": "rank:pairwise", + "A5": "reg:quantileerror", + "A6": "reg:squarederror", + "A7": "count:poisson", + "A8": "reg:gamma", + "A9": "reg:tweedie", + "A10": "survival:aft", + } + param = { + "objective": objectives[task], + "tree_method": "hist", + "device": device, + "max_depth": 2, + "eta": 0.1, + "nthread": 2, + "seed": 41, + } + d = xgb.DMatrix(x, label=target if task != "A10" else None) + if task == "A4": + d.set_group([8] * 12) + d.set_weight(np.linspace(0.5, 2, 12)) + else: + d.set_weight(weights) + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["quantile_alpha"] = 0.5 + if task == "A6": + param["multi_strategy"] = "multi_output_tree" + if task == "A9": + param["tweedie_variance_power"] = 1.5 + if task == "A10": + d.set_float_info("label_lower_bound", positive) + d.set_float_info( + "label_upper_bound", + np.where(np.arange(96) % 4 == 0, np.inf, positive), + ) + param.update( + aft_loss_distribution="normal", aft_loss_distribution_scale=1.0 + ) + model = xgb.train(param, d, num_boost_round=rounds) + if model.attr("test") is not None: + raise AssertionError("unexpected state") + # Actual build/config is recorded; CUDA host additionally validates GPU visibility. + actual = json.loads(model.save_config())["learner"]["generic_param"][ + "device" + ] + if device == "cuda" and not actual.startswith("cuda"): + raise ValueError("silent CPU fallback") + before = model.predict(d) + model.save_model(model_path) + loaded = xgb.Booster() + loaded.load_model(model_path) + after = loaded.predict(d) + record["effective_config"] = json.loads(model.save_config()) + elif library == "lightgbm": + objectives = { + "A1": "regression", + "A2": "binary", + "A3": "multiclass", + "A4": "lambdarank", + "A5": "quantile", + "A6": "regression", + "A7": "poisson", + "A8": "gamma", + "A9": "tweedie", + } + param = { + "objective": objectives[task], + "device_type": device, + "num_leaves": 4, + "learning_rate": 0.1, + "num_threads": 2, + "min_data_in_leaf": 2, + "verbosity": -1, + "seed": 41, + } + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["alpha"] = 0.5 + if task == "A9": + param["tweedie_variance_power"] = 1.5 + targets = target.T if task == "A6" else [target] + predictions = [] + restored = [] + for k, t in enumerate(targets): + d = lgb.Dataset( + x, + label=t, + weight=weights + if task != "A4" + else np.repeat(np.linspace(0.5, 2, 12), 8), + group=[8] * 12 if task == "A4" else None, + ) + model = lgb.train(param, d, num_boost_round=rounds) + predictions.append(model.predict(x)) + model.save_model(str(model_path)) + restored.append(lgb.Booster(model_file=str(model_path)).predict(x)) + before = np.column_stack(predictions) if task == "A6" else predictions[0] + after = np.column_stack(restored) if task == "A6" else restored[0] + record["effective_config"] = param + else: + losses = { + "A1": "RMSE", + "A2": "Logloss", + "A3": "MultiClass", + "A4": "PairLogit", + "A5": "Quantile:alpha=0.5", + "A6": "MultiRMSE", + "A7": "Poisson", + "A9": "Tweedie:variance_power=1.5", + "A10": "SurvivalAft:dist=Normal;scale=1.0", + "A11": "RMSEWithUncertainty", + } + klass = ( + cb.CatBoostClassifier + if task in ["A2", "A3"] + else cb.CatBoostRanker + if task == "A4" + else cb.CatBoostRegressor + ) + model = klass( + loss_function=losses[task], + iterations=rounds, + depth=2, + learning_rate=0.1, + thread_count=2, + random_seed=41, + task_type="GPU" if device == "cuda" else "CPU", + verbose=False, + allow_writing_files=False, + ) + if task == "A10": + target = np.column_stack( + [positive, np.where(np.arange(96) % 4 == 0, -1, positive)] + ) + if task == "A4": + d = cb.Pool( + x, + target, + group_id=np.repeat(np.arange(12), 8), + group_weight=np.repeat(np.linspace(0.5, 2, 12), 8), + ) + else: + d = cb.Pool(x, target, weight=weights) + model.fit(d) + before = model.predict(d, prediction_type="RawFormulaVal") + model.save_model(str(model_path)) + loaded = klass() + loaded.load_model(str(model_path)) + after = loaded.predict(d, prediction_type="RawFormulaVal") + record["effective_config"] = model.get_all_params() + if not np.isfinite(before).all() or len(before) != 96: + raise ValueError("invalid predictions") + np.testing.assert_allclose(before, after, rtol=1e-7, atol=1e-8) + record.update( + status="pass", + prediction_shape=list(np.shape(before)), + reload_max_abs_error=float(np.max(np.abs(before - after))), + ) + except Exception as exc: + record.update(status="error", reason=f"{type(exc).__name__}: {exc}") + finally: + record["diagnostic_wall_s"] = time.perf_counter() - start + cells.append(record) + return { + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates", + "device": device, + "environment": { + "python": platform.python_version(), + "os": platform.platform(), + "cpu": platform.processor(), + "cpu_count": os.cpu_count(), + "packages": {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + }, + "cells": cells, + } + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--device", choices=["cpu", "cuda"], default="cpu") + p.add_argument("--output", type=Path, required=True) + a = p.parse_args() + result = run(a.device) + result["source_sha"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + result["dirty"] = bool(subprocess.check_output(["git", "status", "--porcelain"])) + result["source_file_sha256"] = ( + __import__("hashlib").sha256(Path(__file__).read_bytes()).hexdigest() + ) + a.output.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + print([(r["library"], r["application"], r["status"]) for r in result["cells"]]) + return int(any(r["status"] == "error" for r in result["cells"])) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/v1/evidence/cpu-capabilities-initial.json b/benchmarks/v1/evidence/cpu-capabilities-initial.json new file mode 100644 index 0000000..f68e65f --- /dev/null +++ b/benchmarks/v1/evidence/cpu-capabilities-initial.json @@ -0,0 +1,1939 @@ +{ + "cells": [ + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.0419478339899797, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.003274124988820404, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5.8171743E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "binary:logistic" + }, + "metrics": [ + { + "name": "logloss" + } + ], + "objective": { + "name": "binary:logistic", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.004988624990801327, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "12" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[1.3698882E-1,2.1029353E-2,-1.5801823E-1]", + "boost_from_average": "1", + "num_class": "3", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "multi:softprob" + }, + "metrics": [ + { + "name": "mlogloss" + } + ], + "objective": { + "name": "multi:softprob", + "softmax_multiclass_param": { + "num_class": "3" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.0031243330013239756, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[6.961444E-11]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "rank:pairwise" + }, + "metrics": [ + { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "ndcg@32" + } + ], + "objective": { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "rank:pairwise" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.002760292001767084, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.1168752E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:quantileerror" + }, + "metrics": [ + { + "name": "quantile", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + ], + "objective": { + "name": "reg:quantileerror", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.0027754590119002387, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0,4.183422E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "2" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "multi_output_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.0024954999971669167, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0.699999988", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.0581717E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "count:poisson" + }, + "metrics": [ + { + "name": "poisson-nloglik" + } + ], + "objective": { + "name": "count:poisson", + "poisson_regression_param": { + "max_delta_step": "0.699999988" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.002986374995089136, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:gamma" + }, + "metrics": [ + { + "name": "gamma-deviance" + } + ], + "objective": { + "name": "reg:gamma", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.002663749997736886, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:tweedie" + }, + "metrics": [ + { + "name": "tweedie-nloglik@1.5" + } + ], + "objective": { + "name": "reg:tweedie", + "tweedie_regression_param": { + "tweedie_variance_power": "1.5" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.0031947500101523474, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "survival:aft" + }, + "metrics": [ + { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "aft-nloglik" + } + ], + "objective": { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "survival:aft" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 1.8339924281463027e-06, + "library": "xgboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.019562375004170462, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.0026638330018613487, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "binary", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.0022274999937508255, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_class": 3, + "num_leaves": 4, + "num_threads": 2, + "objective": "multiclass", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.00877120801305864, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "lambdarank", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.0024533340038033202, + "effective_config": { + "alpha": 0.5, + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "quantile", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.003366874996572733, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.001732375007122755, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "poisson", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.002076707998639904, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "gamma", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.0019718340045074, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "tweedie", + "seed": 41, + "tweedie_variance_power": 1.5, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 2.0000006770715117e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 4.169996827840805e-07, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.10305341599450912, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.0048786669940454885, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "class_names": [ + 0, + 1 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Logloss", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Logloss", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.0038194579974515364, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "class_names": [ + 0, + 1, + 2 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiClass", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiClass", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.002783958989311941, + "library": "catboost", + "reason": "TypeError: CatBoostRanker.predict() got an unexpected keyword argument 'prediction_type'", + "status": "error" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.0033315420005237684, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Quantile:alpha=0.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Exact", + "learning_rate": 0.10000000149011612, + "loss_function": "Quantile:alpha=0.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.002843082998879254, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiRMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiRMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.0027287080010864884, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Poisson", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Poisson", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 1.2499949662014842e-06, + "library": "catboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.003276583011029288, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Tweedie:variance_power=1.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Tweedie:variance_power=1.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.0032780409965198487, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "SurvivalAft:dist=Normal;scale=1.0", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "SurvivalAft:dist=Normal;scale=1.0", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 0.0029026249976595864, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSEWithUncertainty", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSEWithUncertainty", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + } + ], + "device": "cpu", + "dirty": true, + "environment": { + "cpu": "i386", + "cpu_count": 16, + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cycler": "0.12.1", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "graphviz": "0.21", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "matplotlib": "3.11.1", + "mpmath": "1.3.0", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numpy": "2.3.5", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "plotly": "7.0.0", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "typing_extensions": "4.16.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2" + }, + "python": "3.12.12" + }, + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates", + "source_file_sha256": "6ff9426c6543b3778b7e3f21a8eccdf919de8c684d8c3f9f1204a2219855d9ad", + "source_sha": "dc744016687fde2c3347cc4e83c9c9f9e0a0aee8" +} diff --git a/benchmarks/v1/evidence/cpu-capabilities.json b/benchmarks/v1/evidence/cpu-capabilities.json new file mode 100644 index 0000000..0363362 --- /dev/null +++ b/benchmarks/v1/evidence/cpu-capabilities.json @@ -0,0 +1,1986 @@ +{ + "cells": [ + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.020918250011163764, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.004209709004499018, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5.8171743E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "binary:logistic" + }, + "metrics": [ + { + "name": "logloss" + } + ], + "objective": { + "name": "binary:logistic", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.004455624992260709, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "12" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[1.3698882E-1,2.1029353E-2,-1.5801823E-1]", + "boost_from_average": "1", + "num_class": "3", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "multi:softprob" + }, + "metrics": [ + { + "name": "mlogloss" + } + ], + "objective": { + "name": "multi:softprob", + "softmax_multiclass_param": { + "num_class": "3" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.0040352500072913244, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[6.961444E-11]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "rank:pairwise" + }, + "metrics": [ + { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "ndcg@32" + } + ], + "objective": { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "rank:pairwise" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.00348566600587219, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.1168752E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:quantileerror" + }, + "metrics": [ + { + "name": "quantile", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + ], + "objective": { + "name": "reg:quantileerror", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.003933291998691857, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0,4.183422E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "2" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "multi_output_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.003416417006519623, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0.699999988", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.0581717E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "count:poisson" + }, + "metrics": [ + { + "name": "poisson-nloglik" + } + ], + "objective": { + "name": "count:poisson", + "poisson_regression_param": { + "max_delta_step": "0.699999988" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.003125542003544979, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:gamma" + }, + "metrics": [ + { + "name": "gamma-deviance" + } + ], + "objective": { + "name": "reg:gamma", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.0035866249963873997, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:tweedie" + }, + "metrics": [ + { + "name": "tweedie-nloglik@1.5" + } + ], + "objective": { + "name": "reg:tweedie", + "tweedie_regression_param": { + "tweedie_variance_power": "1.5" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.0043615839967969805, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "survival:aft" + }, + "metrics": [ + { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "aft-nloglik" + } + ], + "objective": { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "survival:aft" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 1.7499987734481692e-06, + "library": "xgboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.018110791002982296, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.002793792009470053, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "binary", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.002682165999431163, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_class": 3, + "num_leaves": 4, + "num_threads": 2, + "objective": "multiclass", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.009140750000369735, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "lambdarank", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.002580125001259148, + "effective_config": { + "alpha": 0.5, + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "quantile", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.0038815409934613854, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.0022080409980844706, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "poisson", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.006645084009505808, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "gamma", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.0025829170044744387, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "tweedie", + "seed": 41, + "tweedie_variance_power": 1.5, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 5.4580013966187835e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 8.329952834174037e-07, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.10769495800195727, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.011215291000553407, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "class_names": [ + 0, + 1 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Logloss", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Logloss", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.0052644999959738925, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "class_names": [ + 0, + 1, + 2 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiClass", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiClass", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.004579625005135313, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "PairLogit", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "PairLogit", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.0037707919982494786, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Quantile:alpha=0.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Exact", + "learning_rate": 0.10000000149011612, + "loss_function": "Quantile:alpha=0.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.0035313749976921827, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiRMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiRMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.0034954580041812733, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Poisson", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Poisson", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 1.2079981388524175e-06, + "library": "catboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.0033092499943450093, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Tweedie:variance_power=1.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Tweedie:variance_power=1.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.0038252090016612783, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "SurvivalAft:dist=Normal;scale=1.0", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "SurvivalAft:dist=Normal;scale=1.0", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 0.0030153749976307154, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSEWithUncertainty", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSEWithUncertainty", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + } + ], + "device": "cpu", + "dirty": true, + "environment": { + "cpu": "i386", + "cpu_count": 16, + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cycler": "0.12.1", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "graphviz": "0.21", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "matplotlib": "3.11.1", + "mpmath": "1.3.0", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numpy": "2.3.5", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "plotly": "7.0.0", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "typing_extensions": "4.16.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2" + }, + "python": "3.12.12" + }, + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates", + "source_file_sha256": "b6232a6daae49fc0f8e48983b277f52932c15b409e4c6e2f2bfee0918020b146", + "source_sha": "dc744016687fde2c3347cc4e83c9c9f9e0a0aee8" +} diff --git a/benchmarks/v1/evidence/current-selection-046/README.md b/benchmarks/v1/evidence/current-selection-046/README.md new file mode 100644 index 0000000..f3dd282 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/README.md @@ -0,0 +1,21 @@ +# Current synthetic A6/A13 selection integration + +All 16 configurations pass; the independent mean-standardized-RMSE audit selects +openboost:15. The receipt is sealed and re-audited before releasing test features +for fresh-process selected-model inference. No test labels are scored. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.current_selection_smoke /tmp/openboost-selection-046 +``` + +The seeded synthetic arrays, training-scale JSON, protocol, trial jobs, raw model +bundles, validation predictions, process records, receipt and final predictions +are retained. Summary records source/revision/environment identities, commands +and result hashes; protocol/records bind the full search artifacts. These local +paths describe the original run and can be regenerated in a new empty directory. +Training targets are explicitly bound to training row IDs and the frozen scale. +The constant output channel stays exactly seven after restoration. + +This is internal synthetic integration evidence, not a real quality grid, speed +result, formal D5/E5/E7 evidence or OS-enforced label isolation. Other application +adapters, real searches and final A6 quality aggregation remain open. diff --git a/benchmarks/v1/evidence/current-selection-046/job-0.json b/benchmarks/v1/evidence/current-selection-046/job-0.json new file mode 100644 index 0000000..f4b5cc7 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-0.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 1, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-1.json b/benchmarks/v1/evidence/current-selection-046/job-1.json new file mode 100644 index 0000000..7e2d1fc --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-1.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 2, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-10.json b/benchmarks/v1/evidence/current-selection-046/job-10.json new file mode 100644 index 0000000..8047f7f --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-10.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 3, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-11.json b/benchmarks/v1/evidence/current-selection-046/job-11.json new file mode 100644 index 0000000..04dec4f --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-11.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 4, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-12.json b/benchmarks/v1/evidence/current-selection-046/job-12.json new file mode 100644 index 0000000..a9a6e02 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-12.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 1, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-13.json b/benchmarks/v1/evidence/current-selection-046/job-13.json new file mode 100644 index 0000000..4f118d7 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-13.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-14.json b/benchmarks/v1/evidence/current-selection-046/job-14.json new file mode 100644 index 0000000..d2a7040 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-14.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 3, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-15.json b/benchmarks/v1/evidence/current-selection-046/job-15.json new file mode 100644 index 0000000..cd85822 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-15.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 4, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-2.json b/benchmarks/v1/evidence/current-selection-046/job-2.json new file mode 100644 index 0000000..a4f89ec --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-2.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 3, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-3.json b/benchmarks/v1/evidence/current-selection-046/job-3.json new file mode 100644 index 0000000..f1760c3 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-3.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 4, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-4.json b/benchmarks/v1/evidence/current-selection-046/job-4.json new file mode 100644 index 0000000..8ae7c83 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-4.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 1, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-5.json b/benchmarks/v1/evidence/current-selection-046/job-5.json new file mode 100644 index 0000000..d50b835 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-5.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-6.json b/benchmarks/v1/evidence/current-selection-046/job-6.json new file mode 100644 index 0000000..d05d6bc --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-6.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 3, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-7.json b/benchmarks/v1/evidence/current-selection-046/job-7.json new file mode 100644 index 0000000..b4718af --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-7.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 4, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-8.json b/benchmarks/v1/evidence/current-selection-046/job-8.json new file mode 100644 index 0000000..567410d --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-8.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 1, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/job-9.json b/benchmarks/v1/evidence/current-selection-046/job-9.json new file mode 100644 index 0000000..acf9c5c --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/job-9.json @@ -0,0 +1,17 @@ +{ + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 46, + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 2, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "early_stopping_rounds": 2, + "input_npz": "/private/tmp/openboost-selection-046/worker-input.npz" +} diff --git a/benchmarks/v1/evidence/current-selection-046/protocol.json b/benchmarks/v1/evidence/current-selection-046/protocol.json new file mode 100644 index 0000000..3308cb2 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/protocol.json @@ -0,0 +1,170 @@ +{ + "schema": "openboost-selection-v1", + "application": "A6", + "fold": 0, + "identity": { + "code": "6e1784e61364ba42fe523333c7b124e10614db1aecf7e52022c8c540a7fef51e", + "data": "22dde66f732617ce9885e51907f5f29f7225c06a5e9f6504da3934a06391e429", + "split": "1bfbffa91b58a32471af26a3b6ab5fa2a6aab3ac62c62a5d89ce7461881c593b", + "preprocessing": "b6a297738edf72007704f8eb566b139a708e25711c15c7f5e70e044f56bc8060", + "environment": "fa36f7e7e505ee311727430ea46232f2ead74cab4b02c399ed4f8dae6dcdc6da", + "search_design": "7593ea4bf5ae8da6e656d178af29c394965f1cfdb962d110d69af7f7e4989513" + }, + "train_rows": { + "path": "train-rows.npz", + "sha256": "7945b1c0e613af0433623b8b2649db06524d1743070c6676397be4b2188166be" + }, + "train_targets": { + "path": "train-targets.npz", + "sha256": "b69dc444144a681044db6982d2901452bff0806f6f704267f1ecf5217b33dd4a" + }, + "target_scale": { + "path": "scale.json", + "sha256": "ea7e385f0cc3ad0735275eeb04b0e0df9d547a52a1df0c8c38285a5afef3c68d" + }, + "validation": { + "path": "validation.npz", + "sha256": "c548a1b0200e55021a938fa6dd9a7dfba87581ed8eef21b078b6d4abd9fffc00" + }, + "test_features": { + "path": "test-features.npz", + "sha256": "0994f4214fddbf44c3f0d7a6b6313ce7c7e646fe79800d966b43f745c084b301" + }, + "selection_weights": { + "rmse_0": 0.0572378933775511, + "rmse_1": 91.83206847545476, + "rmse_2": 1.0 + }, + "methods": { + "openboost": [ + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 1, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 2, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 3, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 4, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 1, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 3, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 4, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 1, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 2, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 3, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 4, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 1, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 3, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 4, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + } + ] + } +} diff --git a/benchmarks/v1/evidence/current-selection-046/receipt.json b/benchmarks/v1/evidence/current-selection-046/receipt.json new file mode 100644 index 0000000..ea168b1 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/receipt.json @@ -0,0 +1,362 @@ +{ + "artifacts": { + "openboost:00": { + "log": { + "path": "trial-0/execution.json", + "sha256": "8fe8c87d98d299367240e127fee2a0daf8cf19b3babb28ca5abdeaedcd866c2d" + }, + "model": { + "path": "trial-0/model.bin", + "sha256": "c183bcdcaa26bcb54564904a90f0f0e4fe27ba55a048bbd6765552c791e96ccc" + }, + "prediction": { + "path": "trial-0/predictions.npz", + "sha256": "0d48297c6dc68ff839cc0bf9daa1f2b6b07c35d88310478373d57b88836ce76e" + } + }, + "openboost:01": { + "log": { + "path": "trial-1/execution.json", + "sha256": "dc8ac2175eff9d7ca9e5bd2ac029c21bc08e6721a5d32e311ea8eb2906061675" + }, + "model": { + "path": "trial-1/model.bin", + "sha256": "991dd2c8ce573699c87d93dc8ab10ba87079bafc48293200e34c3a592a9da96f" + }, + "prediction": { + "path": "trial-1/predictions.npz", + "sha256": "6aa94d82e2ebd81c2987e8d19097d1bf538d6c11a386745394c049007af43414" + } + }, + "openboost:02": { + "log": { + "path": "trial-2/execution.json", + "sha256": "3271b16ff8c067e0fc5366e7de6d552c250fded93de28273a0c89b755d2c8e65" + }, + "model": { + "path": "trial-2/model.bin", + "sha256": "95fe4e465adead4d4b4cd27063d803820cfb351eed2fc26e07eda120af704267" + }, + "prediction": { + "path": "trial-2/predictions.npz", + "sha256": "9a7399c215bdc8b53dd84dca481a934ec37d9ff4c2208080f662fa98d7d46c55" + } + }, + "openboost:03": { + "log": { + "path": "trial-3/execution.json", + "sha256": "6be1cf2f1f04de9526fea793be18e49dfd5adb235fc3a9e12bde262994868dfe" + }, + "model": { + "path": "trial-3/model.bin", + "sha256": "376fea27ad06665a57b0814406341151bc510c814963a1aad516328d3e16abea" + }, + "prediction": { + "path": "trial-3/predictions.npz", + "sha256": "dce5afafea701d16d5d6028355dd0dc0d3525f25a7efd9cd49378d6967df4eb3" + } + }, + "openboost:04": { + "log": { + "path": "trial-4/execution.json", + "sha256": "9a14b1ef21fc131534dd2d0270dc60f3e8bcf1d96c3e1b8a7cb29dc0eb8a2165" + }, + "model": { + "path": "trial-4/model.bin", + "sha256": "77f8826c52769c5fbed67a95e0ec5106b8de90abef018e1c2177538268e3af4d" + }, + "prediction": { + "path": "trial-4/predictions.npz", + "sha256": "0e5b1d72e070f0bf09d8b506e6a791a900e06045094f13730fa964e95981c860" + } + }, + "openboost:05": { + "log": { + "path": "trial-5/execution.json", + "sha256": "0eb3d462084154ef0f91b621f3647ce5db5bd03ba78cf065ebde462484b42503" + }, + "model": { + "path": "trial-5/model.bin", + "sha256": "8c75d5c101dca71985bf2a8a6ab8e28efdf04d12c741828b249b55c1f9c029b6" + }, + "prediction": { + "path": "trial-5/predictions.npz", + "sha256": "1c6de7db35fafd31179c06dd01a7624f3df2e8b18db02f6a895ce7a2efca2e57" + } + }, + "openboost:06": { + "log": { + "path": "trial-6/execution.json", + "sha256": "fe308acc3ec9b8adc3646c872096f1f009f7bfb5ed3c3a7f62f6bf10bfb07b21" + }, + "model": { + "path": "trial-6/model.bin", + "sha256": "8117fb27f9787110a39fc63644e5d25ed4192caa0f1f203505596046e4ac0756" + }, + "prediction": { + "path": "trial-6/predictions.npz", + "sha256": "7dbed58c84d6f3b217364a60144925c8c1e3c9a2cb966bae8af69616ba749ef2" + } + }, + "openboost:07": { + "log": { + "path": "trial-7/execution.json", + "sha256": "08e5665b37b7949407edd289972ba8fbe718243a1b00db6f699e55c2d15bf37f" + }, + "model": { + "path": "trial-7/model.bin", + "sha256": "a785681c8058f9d3fe1f36062580e98343f9cd4d42263e8c90366e0e205470dd" + }, + "prediction": { + "path": "trial-7/predictions.npz", + "sha256": "b92b6c179e8841c1fe9f14c4be9a17e9ec2614f1fb95f6a7f94ede9f93f68fdf" + } + }, + "openboost:08": { + "log": { + "path": "trial-8/execution.json", + "sha256": "1fd793f2157e1ba0a61f645e1e23249a547489b17d4817e7d4d6f408ac020b6b" + }, + "model": { + "path": "trial-8/model.bin", + "sha256": "86e92ad30a93021cd662f196f2fb9bfae66b7bf8c10f1875fee796fb617e92dd" + }, + "prediction": { + "path": "trial-8/predictions.npz", + "sha256": "5353337dadadcb3735d9702aad54e246e805570e841aad1a474b532ab99e1912" + } + }, + "openboost:09": { + "log": { + "path": "trial-9/execution.json", + "sha256": "f3488f1661f0cc8b9f93b4323f8381df7c90d76624e769382747187997dc989d" + }, + "model": { + "path": "trial-9/model.bin", + "sha256": "144837267fe6f77cacc0ccacaa12b87095388300d7e166a3f60fb309074e7baf" + }, + "prediction": { + "path": "trial-9/predictions.npz", + "sha256": "d1377727a4cc51b7d278a4e2de52166e8fd411cd10e7e701d3f9bbfaa685a169" + } + }, + "openboost:10": { + "log": { + "path": "trial-10/execution.json", + "sha256": "c20fde5755affc7dbf26296ddead1cb7e7c57391fd79696397d08ec088130190" + }, + "model": { + "path": "trial-10/model.bin", + "sha256": "7ec55ed1e5e25b5b5808aa0f36d8f54cbfbec76666ea739f7999221192c2829f" + }, + "prediction": { + "path": "trial-10/predictions.npz", + "sha256": "a6ad44fb295eb90ecb9a1a485437833a7abf874e14cbd64b4426e62ce9a565ba" + } + }, + "openboost:11": { + "log": { + "path": "trial-11/execution.json", + "sha256": "fd956ce40d9ba0966fc68a481b30566ae2dccb7217ecd7c214b64ca57db3bfc1" + }, + "model": { + "path": "trial-11/model.bin", + "sha256": "d55e37e73dbfe60a0802b67749974f68a13fb9182bc9784ad89d98369351a135" + }, + "prediction": { + "path": "trial-11/predictions.npz", + "sha256": "aad582ac78650a3875b1248a60eebf80419f133f0f24488297fbc20f911b3a74" + } + }, + "openboost:12": { + "log": { + "path": "trial-12/execution.json", + "sha256": "7a8f4aa1facb5c04da9de177db791eb784bc223f8af854819ca69e7ec7cbe61e" + }, + "model": { + "path": "trial-12/model.bin", + "sha256": "cf757d5eba93ab8dfbeada6aa220f75c365a0174a6b51015dee0bf5245a58e59" + }, + "prediction": { + "path": "trial-12/predictions.npz", + "sha256": "9fc653a5dfddf376905feecb1065b56cad1e7db5af912604196f5f7eb08c8398" + } + }, + "openboost:13": { + "log": { + "path": "trial-13/execution.json", + "sha256": "340d95bf3ce0dd2ba3bb4e2aec4e164484500eaa63194be21b54880c14f1d515" + }, + "model": { + "path": "trial-13/model.bin", + "sha256": "745ce8414f74020f78a2f0583d44abd16944b36324f3864f94e72fe5a09c5670" + }, + "prediction": { + "path": "trial-13/predictions.npz", + "sha256": "600e5a681c7bce7b047ff68952614c0759acd1accb03db1d72d6752527915276" + } + }, + "openboost:14": { + "log": { + "path": "trial-14/execution.json", + "sha256": "9caad6305b97ce32e74eba9fabb000dec1b9684faf7db0ebc78f4e490fb94349" + }, + "model": { + "path": "trial-14/model.bin", + "sha256": "fd9f8ba40952897066dbe8729b55d1ef8cd0a87e1451cce3f7c579298dc194d0" + }, + "prediction": { + "path": "trial-14/predictions.npz", + "sha256": "9c331ac5dc4710fe14bf273fd15447a89b374819c98a3c9999ef7ba2aed6485e" + } + }, + "openboost:15": { + "log": { + "path": "trial-15/execution.json", + "sha256": "592c2fd0ea4d1e27cedb3865bc2ae27b7c1cb3ad3b5f28b9be1ec4b38ffd833d" + }, + "model": { + "path": "trial-15/model.bin", + "sha256": "ae7ac62b0c51cd7c13596703c9654245cd4d7e7d0522eacd8d30b1cd57517ce1" + }, + "prediction": { + "path": "trial-15/predictions.npz", + "sha256": "e0e4f009555742e828529b6e0eada543fcce53c7d09ba89d0d1c61f67814ff37" + } + } + }, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "records_sha256": "914748bd66c846aa17bece331db830ae18c23fe9b93ca0f96bb5980551cc68f2", + "schema": "openboost-selection-receipt-v1", + "scores": { + "openboost:00": { + "metrics": { + "rmse_0": 18.20585091444491, + "rmse_1": 0.007800342581866398, + "rmse_2": 0.0 + }, + "selection": 0.5861287158661832 + }, + "openboost:01": { + "metrics": { + "rmse_0": 17.317297521788905, + "rmse_1": 0.007304324648522005, + "rmse_2": 0.0 + }, + "selection": 0.5539922901431694 + }, + "openboost:02": { + "metrics": { + "rmse_0": 17.214491973361064, + "rmse_1": 0.007283680415061124, + "rmse_2": 0.0 + }, + "selection": 0.5513988982497239 + }, + "openboost:03": { + "metrics": { + "rmse_0": 16.888959271055004, + "rmse_1": 0.007287940686039495, + "rmse_2": 0.0 + }, + "selection": 0.5453183727132936 + }, + "openboost:04": { + "metrics": { + "rmse_0": 17.22890183257492, + "rmse_1": 0.007173974434654587, + "rmse_2": 0.0 + }, + "selection": 0.5483156525431933 + }, + "openboost:05": { + "metrics": { + "rmse_0": 15.66052637474885, + "rmse_1": 0.0064124618136605456, + "rmse_2": 0.0 + }, + "selection": 0.4950817237475052 + }, + "openboost:06": { + "metrics": { + "rmse_0": 15.347282913351178, + "rmse_1": 0.00627616643055447, + "rmse_2": 0.0 + }, + "selection": 0.4849331628145117 + }, + "openboost:07": { + "metrics": { + "rmse_0": 14.897716991264742, + "rmse_1": 0.006274655853977856, + "rmse_2": 0.0 + }, + "selection": 0.47630952091911677 + }, + "openboost:08": { + "metrics": { + "rmse_0": 17.34091426698091, + "rmse_1": 0.007232026727886919, + "rmse_2": 0.0 + }, + "selection": 0.5522297918581128 + }, + "openboost:09": { + "metrics": { + "rmse_0": 16.373864292879393, + "rmse_1": 0.007103024085446932, + "rmse_2": 0.0 + }, + "selection": 0.5298302975906296 + }, + "openboost:10": { + "metrics": { + "rmse_0": 16.33970418161779, + "rmse_1": 0.006994320763859304, + "rmse_2": 0.0 + }, + "selection": 0.5258510630313958 + }, + "openboost:11": { + "metrics": { + "rmse_0": 16.325844553002714, + "rmse_1": 0.006989516735589041, + "rmse_2": 0.0 + }, + "selection": 0.5254395764320642 + }, + "openboost:12": { + "metrics": { + "rmse_0": 15.56541353864765, + "rmse_1": 0.006207672798369191, + "rmse_2": 0.0 + }, + "selection": 0.4869983046652207 + }, + "openboost:13": { + "metrics": { + "rmse_0": 13.924450239465143, + "rmse_1": 0.0058103765263029525, + "rmse_2": 0.0 + }, + "selection": 0.4435283643930498 + }, + "openboost:14": { + "metrics": { + "rmse_0": 13.755036593667661, + "rmse_1": 0.005636801628468608, + "rmse_2": 0.0 + }, + "selection": 0.43498282369358243 + }, + "openboost:15": { + "metrics": { + "rmse_0": 13.734679838124201, + "rmse_1": 0.005610738864455218, + "rmse_2": 0.0 + }, + "selection": 0.4337966319159669 + } + }, + "selected": "openboost:15" +} diff --git a/benchmarks/v1/evidence/current-selection-046/records.json b/benchmarks/v1/evidence/current-selection-046/records.json new file mode 100644 index 0000000..5dd9d12 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/records.json @@ -0,0 +1,418 @@ +[ + { + "id": "openboost:00", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 1, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-0/predictions.npz", + "sha256": "0d48297c6dc68ff839cc0bf9daa1f2b6b07c35d88310478373d57b88836ce76e" + }, + "model": { + "path": "trial-0/model.bin", + "sha256": "c183bcdcaa26bcb54564904a90f0f0e4fe27ba55a048bbd6765552c791e96ccc" + }, + "log": { + "path": "trial-0/execution.json", + "sha256": "8fe8c87d98d299367240e127fee2a0daf8cf19b3babb28ca5abdeaedcd866c2d" + } + }, + { + "id": "openboost:01", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 2, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-1/predictions.npz", + "sha256": "6aa94d82e2ebd81c2987e8d19097d1bf538d6c11a386745394c049007af43414" + }, + "model": { + "path": "trial-1/model.bin", + "sha256": "991dd2c8ce573699c87d93dc8ab10ba87079bafc48293200e34c3a592a9da96f" + }, + "log": { + "path": "trial-1/execution.json", + "sha256": "dc8ac2175eff9d7ca9e5bd2ac029c21bc08e6721a5d32e311ea8eb2906061675" + } + }, + { + "id": "openboost:02", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 3, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-2/predictions.npz", + "sha256": "9a7399c215bdc8b53dd84dca481a934ec37d9ff4c2208080f662fa98d7d46c55" + }, + "model": { + "path": "trial-2/model.bin", + "sha256": "95fe4e465adead4d4b4cd27063d803820cfb351eed2fc26e07eda120af704267" + }, + "log": { + "path": "trial-2/execution.json", + "sha256": "3271b16ff8c067e0fc5366e7de6d552c250fded93de28273a0c89b755d2c8e65" + } + }, + { + "id": "openboost:03", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 4, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-3/predictions.npz", + "sha256": "dce5afafea701d16d5d6028355dd0dc0d3525f25a7efd9cd49378d6967df4eb3" + }, + "model": { + "path": "trial-3/model.bin", + "sha256": "376fea27ad06665a57b0814406341151bc510c814963a1aad516328d3e16abea" + }, + "log": { + "path": "trial-3/execution.json", + "sha256": "6be1cf2f1f04de9526fea793be18e49dfd5adb235fc3a9e12bde262994868dfe" + } + }, + { + "id": "openboost:04", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 1, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-4/predictions.npz", + "sha256": "0e5b1d72e070f0bf09d8b506e6a791a900e06045094f13730fa964e95981c860" + }, + "model": { + "path": "trial-4/model.bin", + "sha256": "77f8826c52769c5fbed67a95e0ec5106b8de90abef018e1c2177538268e3af4d" + }, + "log": { + "path": "trial-4/execution.json", + "sha256": "9a14b1ef21fc131534dd2d0270dc60f3e8bcf1d96c3e1b8a7cb29dc0eb8a2165" + } + }, + { + "id": "openboost:05", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-5/predictions.npz", + "sha256": "1c6de7db35fafd31179c06dd01a7624f3df2e8b18db02f6a895ce7a2efca2e57" + }, + "model": { + "path": "trial-5/model.bin", + "sha256": "8c75d5c101dca71985bf2a8a6ab8e28efdf04d12c741828b249b55c1f9c029b6" + }, + "log": { + "path": "trial-5/execution.json", + "sha256": "0eb3d462084154ef0f91b621f3647ce5db5bd03ba78cf065ebde462484b42503" + } + }, + { + "id": "openboost:06", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 3, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-6/predictions.npz", + "sha256": "7dbed58c84d6f3b217364a60144925c8c1e3c9a2cb966bae8af69616ba749ef2" + }, + "model": { + "path": "trial-6/model.bin", + "sha256": "8117fb27f9787110a39fc63644e5d25ed4192caa0f1f203505596046e4ac0756" + }, + "log": { + "path": "trial-6/execution.json", + "sha256": "fe308acc3ec9b8adc3646c872096f1f009f7bfb5ed3c3a7f62f6bf10bfb07b21" + } + }, + { + "id": "openboost:07", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 4, + "mode": "shared", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-7/predictions.npz", + "sha256": "b92b6c179e8841c1fe9f14c4be9a17e9ec2614f1fb95f6a7f94ede9f93f68fdf" + }, + "model": { + "path": "trial-7/model.bin", + "sha256": "a785681c8058f9d3fe1f36062580e98343f9cd4d42263e8c90366e0e205470dd" + }, + "log": { + "path": "trial-7/execution.json", + "sha256": "08e5665b37b7949407edd289972ba8fbe718243a1b00db6f699e55c2d15bf37f" + } + }, + { + "id": "openboost:08", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 1, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-8/predictions.npz", + "sha256": "5353337dadadcb3735d9702aad54e246e805570e841aad1a474b532ab99e1912" + }, + "model": { + "path": "trial-8/model.bin", + "sha256": "86e92ad30a93021cd662f196f2fb9bfae66b7bf8c10f1875fee796fb617e92dd" + }, + "log": { + "path": "trial-8/execution.json", + "sha256": "1fd793f2157e1ba0a61f645e1e23249a547489b17d4817e7d4d6f408ac020b6b" + } + }, + { + "id": "openboost:09", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 2, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-9/predictions.npz", + "sha256": "d1377727a4cc51b7d278a4e2de52166e8fd411cd10e7e701d3f9bbfaa685a169" + }, + "model": { + "path": "trial-9/model.bin", + "sha256": "144837267fe6f77cacc0ccacaa12b87095388300d7e166a3f60fb309074e7baf" + }, + "log": { + "path": "trial-9/execution.json", + "sha256": "f3488f1661f0cc8b9f93b4323f8381df7c90d76624e769382747187997dc989d" + } + }, + { + "id": "openboost:10", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 3, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-10/predictions.npz", + "sha256": "a6ad44fb295eb90ecb9a1a485437833a7abf874e14cbd64b4426e62ce9a565ba" + }, + "model": { + "path": "trial-10/model.bin", + "sha256": "7ec55ed1e5e25b5b5808aa0f36d8f54cbfbec76666ea739f7999221192c2829f" + }, + "log": { + "path": "trial-10/execution.json", + "sha256": "c20fde5755affc7dbf26296ddead1cb7e7c57391fd79696397d08ec088130190" + } + }, + { + "id": "openboost:11", + "config": { + "rounds": 4, + "learning_rate": 0.05, + "max_depth": 4, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-11/predictions.npz", + "sha256": "aad582ac78650a3875b1248a60eebf80419f133f0f24488297fbc20f911b3a74" + }, + "model": { + "path": "trial-11/model.bin", + "sha256": "d55e37e73dbfe60a0802b67749974f68a13fb9182bc9784ad89d98369351a135" + }, + "log": { + "path": "trial-11/execution.json", + "sha256": "fd956ce40d9ba0966fc68a481b30566ae2dccb7217ecd7c214b64ca57db3bfc1" + } + }, + { + "id": "openboost:12", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 1, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-12/predictions.npz", + "sha256": "9fc653a5dfddf376905feecb1065b56cad1e7db5af912604196f5f7eb08c8398" + }, + "model": { + "path": "trial-12/model.bin", + "sha256": "cf757d5eba93ab8dfbeada6aa220f75c365a0174a6b51015dee0bf5245a58e59" + }, + "log": { + "path": "trial-12/execution.json", + "sha256": "7a8f4aa1facb5c04da9de177db791eb784bc223f8af854819ca69e7ec7cbe61e" + } + }, + { + "id": "openboost:13", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-13/predictions.npz", + "sha256": "600e5a681c7bce7b047ff68952614c0759acd1accb03db1d72d6752527915276" + }, + "model": { + "path": "trial-13/model.bin", + "sha256": "745ce8414f74020f78a2f0583d44abd16944b36324f3864f94e72fe5a09c5670" + }, + "log": { + "path": "trial-13/execution.json", + "sha256": "340d95bf3ce0dd2ba3bb4e2aec4e164484500eaa63194be21b54880c14f1d515" + } + }, + { + "id": "openboost:14", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 3, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-14/predictions.npz", + "sha256": "9c331ac5dc4710fe14bf273fd15447a89b374819c98a3c9999ef7ba2aed6485e" + }, + "model": { + "path": "trial-14/model.bin", + "sha256": "fd9f8ba40952897066dbe8729b55d1ef8cd0a87e1451cce3f7c579298dc194d0" + }, + "log": { + "path": "trial-14/execution.json", + "sha256": "9caad6305b97ce32e74eba9fabb000dec1b9684faf7db0ebc78f4e490fb94349" + } + }, + { + "id": "openboost:15", + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 4, + "mode": "independent", + "bins": 16, + "reg_lambda": 1.0 + }, + "status": "pass", + "exit_code": 0, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "prediction": { + "path": "trial-15/predictions.npz", + "sha256": "e0e4f009555742e828529b6e0eada543fcce53c7d09ba89d0d1c61f67814ff37" + }, + "model": { + "path": "trial-15/model.bin", + "sha256": "ae7ac62b0c51cd7c13596703c9654245cd4d7e7d0522eacd8d30b1cd57517ce1" + }, + "log": { + "path": "trial-15/execution.json", + "sha256": "592c2fd0ea4d1e27cedb3865bc2ae27b7c1cb3ad3b5f28b9be1ec4b38ffd833d" + } + } +] diff --git a/benchmarks/v1/evidence/current-selection-046/released-features.npz b/benchmarks/v1/evidence/current-selection-046/released-features.npz new file mode 100644 index 0000000..4b20a8a Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/released-features.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/scale.json b/benchmarks/v1/evidence/current-selection-046/scale.json new file mode 100644 index 0000000..f49f28f --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/scale.json @@ -0,0 +1,17 @@ +{ + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] +} diff --git a/benchmarks/v1/evidence/current-selection-046/selected-predictions.npz b/benchmarks/v1/evidence/current-selection-046/selected-predictions.npz new file mode 100644 index 0000000..0aa7137 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/selected-predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/summary.json b/benchmarks/v1/evidence/current-selection-046/summary.json new file mode 100644 index 0000000..3469bb7 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/summary.json @@ -0,0 +1,373 @@ +{ + "scope": "Synthetic A6/A13 selection integration only; not real quality, cost or OS isolation", + "revision": "e83e7b528cf87c3e062b14177e2545d50ae9e2af", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "benchmarks.v1.current_selection_smoke", + "/private/tmp/openboost-selection-046" + ], + "sources": { + "src/openboost/__init__.py": "b0a76b68a1824abef45bf30d3d094dce7de2ec9deef3d5cbe862a6b3f6038788", + "src/openboost/artifacts.py": "fc5232f29d2b9d55676035bbd5bb9fe726babba5fcdfbd84ff18133de3e9a817", + "src/openboost/binning.py": "e77bdbaf42121ba5dde8ec681371bd0c7d1e31fada19618d796279cc0b322a7d", + "src/openboost/composition.py": "a52dc4d09582031441dbf2c4a3ed889851783a2f4fece50506fa5e0e0501ccec", + "src/openboost/data.py": "93b4eb6487e7fb21c960d19906c7a49b3df9fa28b2a2bacdc8ea6437889f2343", + "src/openboost/leaves.py": "3bff04ff4b1eff1a1d58f6ce5e29f15391e8b2e9a80a696c6fee988d1be95b75", + "src/openboost/multioutput.py": "98c192719bb9ee787ec385a2d219a2f52aacebc161cc9fc1275006f49daad76d", + "src/openboost/objectives.py": "fcf847bda2917354093e03ce42bd22e0fd85e391185d3141318cb7ccecd721f7", + "src/openboost/ops.py": "aa73cc40df7c79203505f91e3b86292dffbb3b9348467be60d8587e3bfd8c6ad", + "src/openboost/outputs.py": "f10f3a68b4513cb93c222cd1e39ca238c686c903daa8c8035c417b5916365115", + "src/openboost/ranking.py": "beddf8fb6eeecbed54acdb55147bc251392a6f9fb7b46304e5d80d1d0ee642b3", + "src/openboost/recipes.py": "4fc1cd9971cde4bad95922c0f4e241d169a8a9fa08730070f02033c3405d265c", + "src/openboost/results.py": "b33bb8c658678bf5319a1e76b9a420112debea4f26361acad166f6f1e641585d", + "src/openboost/runs.py": "5a00eead8d6d4d6ddc99a646a0131f04918c619c5061b647ec4fc3c386459dc4", + "src/openboost/runtime.py": "8962e0204d867a1b700ed15e92b155cd6144927b651a79b8083634a0c315ad2d", + "src/openboost/stats.py": "4edbeba39e727f60213f04b5d3592bc82fb02cb1ebd9603ca9f8cee66b932cc9", + "src/openboost/stopping.py": "2374873a01dba8d7b359cd484337de228ca6c805d6a43b23bcf51598416746a1", + "src/openboost/survival.py": "cb168551a5aa026d6c501f4370c7ddc998d3cc7269ce6f17d93258ceadef89e9", + "src/openboost/tree.py": "60dd8cbc56d78913e868a3788bd08c91e858febc4f4c0534831077df640d7581", + "benchmarks/v1/current_selection_smoke.py": "67e3651f1c42c78e7d4fb162736bf4155c9259102fcace405fc24c011234bd23", + "benchmarks/v1/openboost_worker.py": "a467fc8024eebad0fa9e1412e20e9c374f72cb96b35b08890434a669a44b4085", + "benchmarks/v1/openboost_predict.py": "844af41520e2f6de1e0c7f7d50bea13777867b860de83f385e529780661903c9", + "benchmarks/v1/selection.py": "22aca8f1234b5e13ea71f387fd3acca3b58c501f35264762491dc8220ce64f2e", + "benchmarks/v1/preprocessing.py": "62561effccd6856bc404339dddbc8c2daa3a1d0b011be59edd8fd3435802bc30", + "benchmarks/v1/quality.py": "6736b97666f73a0c1831fd5c9472a4fd66cc675d2b66884a684cdad92354e827", + "benchmarks/v1/quality_report.py": "ab2bf28a5b6f871fb13fe91b4d9d396d547531b184b85c5c4816f5c74c327a7b", + "benchmarks/v1/process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "environment": { + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "threads": 1, + "cpu_count": 16, + "gpu": null, + "memory_cap": null, + "packages": { + "openboost": "1.0.0rc1", + "numpy": "2.3.5" + } + }, + "protocol_sha256": "7409a9484e69dd3605008245a7afe80a60135f3fb7b917df803b72e8059bd094", + "passed": true, + "trials": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-0.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.18943250000302214, + "artifacts": { + "model.bin": "c183bcdcaa26bcb54564904a90f0f0e4fe27ba55a048bbd6765552c791e96ccc", + "training.json": "27edd78a37f37aaae4689e3d217b640dc47b0de158b88276d69f0c4829fabd9c", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0d48297c6dc68ff839cc0bf9daa1f2b6b07c35d88310478373d57b88836ce76e" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-1.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.2466095419949852, + "artifacts": { + "model.bin": "991dd2c8ce573699c87d93dc8ab10ba87079bafc48293200e34c3a592a9da96f", + "training.json": "bd71970f2ed2f63198376412c5ba5b2ac8b42b1d924bd194d6165c43ed9fa2fc", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6aa94d82e2ebd81c2987e8d19097d1bf538d6c11a386745394c049007af43414" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-2.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.34371020800608676, + "artifacts": { + "model.bin": "95fe4e465adead4d4b4cd27063d803820cfb351eed2fc26e07eda120af704267", + "training.json": "59b3ca7f5c5a144192498ae32dc0ac64b693f14fa762f475c69e0bdf015c666a", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "9a7399c215bdc8b53dd84dca481a934ec37d9ff4c2208080f662fa98d7d46c55" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-3.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.4640418749913806, + "artifacts": { + "model.bin": "376fea27ad06665a57b0814406341151bc510c814963a1aad516328d3e16abea", + "training.json": "1050e95ead4482a23010ce3004f06991ac5dde5ac232ed939c2b863f475cc1ff", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "dce5afafea701d16d5d6028355dd0dc0d3525f25a7efd9cd49378d6967df4eb3" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-4.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.18671891700068954, + "artifacts": { + "model.bin": "77f8826c52769c5fbed67a95e0ec5106b8de90abef018e1c2177538268e3af4d", + "training.json": "d8f051889b1b68518e825937cff19242e0a24ca4a17038286ccef753a67fd339", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0e5b1d72e070f0bf09d8b506e6a791a900e06045094f13730fa964e95981c860" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-5.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.24036341600003652, + "artifacts": { + "model.bin": "8c75d5c101dca71985bf2a8a6ab8e28efdf04d12c741828b249b55c1f9c029b6", + "training.json": "7970cd42c8af1c6c643b2aabd94fea310b09600f529787c58911e12d759e6fec", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1c6de7db35fafd31179c06dd01a7624f3df2e8b18db02f6a895ce7a2efca2e57" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-6.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.3485678330034716, + "artifacts": { + "model.bin": "8117fb27f9787110a39fc63644e5d25ed4192caa0f1f203505596046e4ac0756", + "training.json": "7192a11d4e71fbccba6cd6cc65d7d3daa5fb8acb5b63942c8847ffa544f3bb94", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "7dbed58c84d6f3b217364a60144925c8c1e3c9a2cb966bae8af69616ba749ef2" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-7.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.5652502919983817, + "artifacts": { + "model.bin": "a785681c8058f9d3fe1f36062580e98343f9cd4d42263e8c90366e0e205470dd", + "training.json": "026acdc310bc272e343936d8ab91bc0c4065727556aa563f5f7f9ba7619f01db", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "b92b6c179e8841c1fe9f14c4be9a17e9ec2614f1fb95f6a7f94ede9f93f68fdf" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-8.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.189344667000114, + "artifacts": { + "model.bin": "86e92ad30a93021cd662f196f2fb9bfae66b7bf8c10f1875fee796fb617e92dd", + "training.json": "3cb01852d95d64b4a64bb8bac8f73779a9544571b95547cb5275b8e06443bc01", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "5353337dadadcb3735d9702aad54e246e805570e841aad1a474b532ab99e1912" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-9.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.24095179200230632, + "artifacts": { + "model.bin": "144837267fe6f77cacc0ccacaa12b87095388300d7e166a3f60fb309074e7baf", + "training.json": "5056d916abf4a3b3a084568ab62c28397e02cf57783852f3094ec30a9ce065bc", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "d1377727a4cc51b7d278a4e2de52166e8fd411cd10e7e701d3f9bbfaa685a169" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-10.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.3485011669981759, + "artifacts": { + "model.bin": "7ec55ed1e5e25b5b5808aa0f36d8f54cbfbec76666ea739f7999221192c2829f", + "training.json": "61128b6d3f20845f00ead4d27dccfdfd336b73267cd2e9caf4fc87763eba2dce", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "a6ad44fb295eb90ecb9a1a485437833a7abf874e14cbd64b4426e62ce9a565ba" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-11.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.40403341699857265, + "artifacts": { + "model.bin": "d55e37e73dbfe60a0802b67749974f68a13fb9182bc9784ad89d98369351a135", + "training.json": "1d2d39d45c1e39255654f1881ef067fd94c0036f74356ffe50fa71eb8402adb7", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "aad582ac78650a3875b1248a60eebf80419f133f0f24488297fbc20f911b3a74" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-12.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.1899553749972256, + "artifacts": { + "model.bin": "cf757d5eba93ab8dfbeada6aa220f75c365a0174a6b51015dee0bf5245a58e59", + "training.json": "c9996f5b9eba2a68ff41c1d64b7cbe0dbb66e87bb831f273e1db22a4f4a18da5", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "9fc653a5dfddf376905feecb1065b56cad1e7db5af912604196f5f7eb08c8398" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-13.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.24297970900079235, + "artifacts": { + "model.bin": "745ce8414f74020f78a2f0583d44abd16944b36324f3864f94e72fe5a09c5670", + "training.json": "ab084eca35b01d7b7e73468eb133d3a1ca02e9253450cf226c62e8bbf23165df", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "600e5a681c7bce7b047ff68952614c0759acd1accb03db1d72d6752527915276" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-14.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.35101504198974, + "artifacts": { + "model.bin": "fd9f8ba40952897066dbe8729b55d1ef8cd0a87e1451cce3f7c579298dc194d0", + "training.json": "5b256f0e82652846d71544640ba1f115aad6c4275799cbc26b07f853e8fedbd5", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "9c331ac5dc4710fe14bf273fd15447a89b374819c98a3c9999ef7ba2aed6485e" + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-15.json" + ], + "timeout_s": 60, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.4539316250011325, + "artifacts": { + "model.bin": "ae7ac62b0c51cd7c13596703c9654245cd4d7e7d0522eacd8d30b1cd57517ce1", + "training.json": "885f3313546ff3fb8a2fe015c80c29e223f8a29635bff235f903f39359a059c2", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "e0e4f009555742e828529b6e0eada543fcce53c7d09ba89d0d1c61f67814ff37" + } + } + ], + "selected": "openboost:15", + "receipt_sha256": "f29134548b45d36f708b9ca3a3695121d1e57c811bbb252dc3f6f7a15950dfa2", + "inference_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-selection-046/trial-15/model.bin", + "/private/tmp/openboost-selection-046/released-features.npz", + "/private/tmp/openboost-selection-046/selected-predictions.npz" + ], + "selected_predictions": { + "path": "selected-predictions.npz", + "sha256": "7d0d8b01149168afc295e1aab0df6bad445b237f9f0fa0bcaa6a1933f7df9f06" + } +} diff --git a/benchmarks/v1/evidence/current-selection-046/test-features.npz b/benchmarks/v1/evidence/current-selection-046/test-features.npz new file mode 100644 index 0000000..4b20a8a Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/test-features.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/train-rows.npz b/benchmarks/v1/evidence/current-selection-046/train-rows.npz new file mode 100644 index 0000000..570c4c7 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/train-rows.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/train-targets.npz b/benchmarks/v1/evidence/current-selection-046/train-targets.npz new file mode 100644 index 0000000..3c5671d Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/train-targets.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-0/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-0/execution.json new file mode 100644 index 0000000..fa5e22c --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-0/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "c183bcdcaa26bcb54564904a90f0f0e4fe27ba55a048bbd6765552c791e96ccc", + "predictions.npz": "0d48297c6dc68ff839cc0bf9daa1f2b6b07c35d88310478373d57b88836ce76e", + "training.json": "27edd78a37f37aaae4689e3d217b640dc47b0de158b88276d69f0c4829fabd9c", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-0.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.18943250000302214 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-0/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-0/model.bin new file mode 100644 index 0000000..27873f6 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-0/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [6, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0, -1.878838964750265e-17, -0.0], [0.2956387665128784, -0.8516864204451935, -0.0], [-0.2317168710506345, 0.6675380052138005, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[4.9170688817100884e-05, -0.00014165262710109222, -0.0], [-0.7854321193044503, 0.12129734570493018, -0.0], [0.7855289706612112, -0.12157635845528084, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [9, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[4.678665541992298e-05, -0.00013478462093860713, -0.0], [-0.5905112639653753, 0.07392194585629061, -0.0], [0.9685601182073077, -0.12158243121875699, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-0/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-0/predictions.npz new file mode 100644 index 0000000..2c9f60e Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-0/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-0/training.json b/benchmarks/v1/evidence/current-selection-046/trial-0/training.json new file mode 100644 index 0000000..4022b11 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-0/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.7995075799123932, + "last_score": 0.7995075799123932, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "7323cc64d9028e560c881d8d2aea26f1e40e49578483944491f91ca49be1e9fc", + "best_validation_score": 0.7995075799123932, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-0/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-0/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-1/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-1/execution.json new file mode 100644 index 0000000..2275043 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-1/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "991dd2c8ce573699c87d93dc8ab10ba87079bafc48293200e34c3a592a9da96f", + "predictions.npz": "6aa94d82e2ebd81c2987e8d19097d1bf538d6c11a386745394c049007af43414", + "training.json": "bd71970f2ed2f63198376412c5ba5b2ac8b42b1d924bd194d6165c43ed9fa2fc", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-1.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.2466095419949852 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-1/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-1/model.bin new file mode 100644 index 0000000..8a8cae9 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-1/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [7, 11, 5, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0], [-0.26036447312296984, -0.6621493740935572, -0.0], [1.3837394915426051, -0.9863847343809521, -0.0], [-1.0431332214669604, 0.7388624736089101, -0.0], [0.5000301905763517, 0.7952356253524009, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [7, 11, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.00044636306733000656, -8.802769962555656e-05, -0.0], [0.2674934151455852, -0.7532760910196639, -0.0], [-0.2666142151644806, 0.753102703126462, -0.0], [-0.24791225919100174, -0.6304813605499523, -0.0], [1.3208422419270318, -0.9415490646363636, -0.0], [-0.8703890295765084, 0.659313078370081, -0.0], [0.6149651065073075, 0.8332948311265441, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [6, 10, 5, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0010528957290918543, -0.00014472055748250963, -0.0], [0.2782887900057442, -0.8164821302180061, -0.0], [-0.21626855912907048, 0.6396912145969136, -0.0], [-0.30518255947722395, -0.7258798209284397, -0.0], [1.2608039582030761, -0.8987513798801653, -0.0], [-0.950229582113362, 0.6373656401848086, -0.0], [0.4551097895132453, 0.6097996708379628, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [7, 11, 6, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0013544829473494029, -0.00042784290636671036, -0.0], [0.24433552724416802, -0.6883175054116674, -0.0], [-0.24166760628726763, 0.6874747845354907, -0.0], [-0.22501605052498935, -0.5772864430366649, -0.0], [1.2034946873756633, -0.8578990444310667, -0.0], [-0.8695802910673844, 0.633172545339569, -0.0], [0.4798383894833179, 0.7055976295974343, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-1/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-1/predictions.npz new file mode 100644 index 0000000..d82dd28 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-1/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-1/training.json b/benchmarks/v1/evidence/current-selection-046/trial-1/training.json new file mode 100644 index 0000000..7b3b44e --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-1/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.7162113286897497, + "last_score": 0.7162113286897497, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "77928918d0d965ac3e6099300edc31983e3ca22bbeac8fbf48341b27e0eb4c0e", + "best_validation_score": 0.7162113286897497, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-1/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-1/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-10/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-10/execution.json new file mode 100644 index 0000000..875a15c --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-10/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "7ec55ed1e5e25b5b5808aa0f36d8f54cbfbec76666ea739f7999221192c2829f", + "predictions.npz": "a6ad44fb295eb90ecb9a1a485437833a7abf874e14cbd64b4426e62ce9a565ba", + "training.json": "61128b6d3f20845f00ead4d27dccfdfd336b73267cd2e9caf4fc87763eba2dce", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-10.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.3485011669981759 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-10/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-10/model.bin new file mode 100644 index 0000000..7748a5c --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-10/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916], [-1.1796216992510447], [-0.28343107662342826], [0.4395337249025689], [1.4256575517704677], [-0.4181223204361509], [-0.05738471593578133], [0.22998810011862172], [0.6002422691417862]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, 5, 9, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785], [-1.2080578843704168], [-0.3260402145908945], [0.4234637259088333], [1.3220714952505224], [-0.4903954777096544], [-0.12545826096203508], [0.18783091914567335], [0.554019997828803]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[0.00046212245031377247], [-0.6699485637384571], [0.8557977523308038], [-1.1241100898745249], [-0.27039168240266626], [0.419998892684677], [1.3598579724579845], [-0.39882436718525166], [-0.054834284116413286], [0.21976640678001633], [0.5735648349577069]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 8, 13, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00018462368398680908], [-0.7535418145265843], [0.7539054672374675], [-1.1512081015765148], [-0.31154953838685473], [0.40405685132047364], [1.2610528108543444], [-1.3561000891667763], [-0.8184041026999738], [-0.46860012314478094], [-0.11988233825261127], [0.06779076406112443], [0.4791905916131956], [0.7317764037927277], [1.414978280238093]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[0.000881647460890015], [-0.6385832554720426], [0.8167202599111527], [-1.0712107915274884], [-0.2579522379284893], [0.40133227523202464], [1.2970952968060776], [-0.3804170886997785], [-0.05239720482235047], [0.2099990109231267], [0.5480730645151421]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, 13, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00012212279704977805], [-0.7200510672142916], [0.7202916121175718], [-1.1000432970620029], [-0.2977028922363279], [0.38515634531557935], [1.206256919096361], [-1.2958289740926974], [-0.7820305870244195], [-0.4477734510050129], [-0.11455423433027304], [0.16732773705754742], [0.5063333552391722], [0.7025053476410187], [1.3520903566719555]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 5, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[0.001261520693379918], [-0.6086865590295885], [0.7794276389367059], [-1.0208008719261947], [-0.24608513625473652], [0.38349528522171245], [1.2372293600304123], [-0.4209144506887638], [-0.10611983116543021], [0.2006657215487655], [0.5237142616478024]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00018376246501057293], [-0.6880487975603232], [0.6884107539307985], [-1.0511524838592474], [-0.28447165258137985], [0.3675024830314592], [1.1538463643119776], [-1.2382365752441329], [-0.7472736720455563], [-0.42787240873812343], [-0.10946293502670533], [0.15989094874387863], [0.4829641234589027]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-10/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-10/predictions.npz new file mode 100644 index 0000000..67eb5da Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-10/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-10/training.json b/benchmarks/v1/evidence/current-selection-046/trial-10/training.json new file mode 100644 index 0000000..b38ed92 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-10/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.6436230466078394, + "last_score": 0.6436230466078394, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "5d48fc2ed79205f395702d8a7a29b7b2c1f44444019423afda75a3216f81c63f", + "best_validation_score": 0.6436230466078394, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-10/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-10/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-11/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-11/execution.json new file mode 100644 index 0000000..6b1bae0 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-11/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "d55e37e73dbfe60a0802b67749974f68a13fb9182bc9784ad89d98369351a135", + "predictions.npz": "aad582ac78650a3875b1248a60eebf80419f133f0f24488297fbc20f911b3a74", + "training.json": "1d2d39d45c1e39255654f1881ef067fd94c0036f74356ffe50fa71eb8402adb7", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-11.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.40403341699857265 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-11/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-11/model.bin new file mode 100644 index 0000000..fd0dfed --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-11/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916], [-1.1796216992510447], [-0.28343107662342826], [0.4395337249025689], [1.4256575517704677], [-0.4181223204361509], [-0.05738471593578133], [0.22998810011862172], [0.6002422691417862], [-0.4741306185252607], [-0.23368291978852299], [-0.11367372000521055], [0.010381231320804147], [0.2444359897228614], [0.057202491642352235]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, 1, 1, -1, -1, 1, 2, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, 5, 9, -1, -1, 6, 5, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, 11, 13, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, 12, 14, -1, -1, -1, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785], [-1.2080578843704168], [-0.3260402145908945], [0.4234637259088333], [1.3220714952505224], [-0.4903954777096544], [-0.12545826096203508], [0.18783091914567335], [0.554019997828803], [-0.17095460066520288], [-0.05487026906646029], [0.07530400082695135], [0.2627916536352607]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0002590850584832506], [-0.670402075887952], [0.855921287470884], [-1.1241100898745249], [-0.2711907276184431], [0.4202096291001081], [1.3598579724579845], [-0.3999385641770887], [-0.05508932729835009], [0.22016446445360852], [0.5735648349577069], [-0.4530581465908048], [-0.22433560299698202], [-0.10912677120500214], [0.009965982067971982], [0.23374191517248621], [0.05577242935129343]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, 13, -1, -1, -1, 6, 8, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, 15, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, 16, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00022300685825364075], [-0.7536938851459323], [0.7541331410788562], [-1.1512081015765148], [-0.31184473429500065], [0.40441462449979887], [1.2610528108543444], [-1.3561000891667763], [-0.8184041026999738], [-0.46860012314478094], [-0.12043993052355367], [0.18031768237984644], [0.5284498440828582], [0.7317764037927277], [1.414978280238093], [-0.16411641663859475], [-0.05267545830380187], [0.07229184079387331], [0.25227998748985025]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0004940327465899504], [-0.6394504378078778], [0.8169578733593045], [-1.0712107915274884], [-0.2594801306154371], [0.40173761582004835], [1.2970952968060776], [-0.38254699654357194], [-0.052885754206416086], [0.21076465425606017], [0.5480730645151421], [-0.43292222896454674], [-0.2153621788771028], [-0.10476170035680206], [0.0095673427852531], [0.22351570638368992], [0.054378118617511094]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, 13, -1, -1, -1, 6, 8, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, 15, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, 16, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00031945664337444435], [-0.7203423669340202], [0.7209715997164245], [-1.1000432970620029], [-0.29826835639815413], [0.38622489725663406], [1.206256919096361], [-1.2958289740926974], [-0.7820305870244195], [-0.4477734510050129], [-0.11562233330261153], [0.17310497508465253], [0.504059851279034], [0.7025053476410187], [1.3520903566719555], [-0.15755175997305096], [-0.050568439971649795], [0.06940016716211837], [0.24218878799025623]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[0.000706540824169489], [-0.609930199102539], [0.7797704317367227], [-1.0208008719261947], [-0.24827631162136332], [0.38408004940997625], [1.2372293600304123], [-0.3659130482850151], [-0.05077032403815943], [0.20177027612659718], [0.5237142616478024], [-0.41368124101056686], [-0.20674769172201865], [-0.10057123234252996], [0.00918464907384298], [0.21373689422940348], [0.05301866565207332]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 8, -1, -1, -1, -1, 6, -1, 10, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, -1, -1, -1, -1, 13, -1, 15, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, -1, -1, -1, -1, 14, -1, 16, -1, -1, -1, -1], "value": [[0.0004084771178807234], [-0.6884672989086327], [0.6892718750499129], [-1.0511524838592474], [-0.2852840375516279], [0.36885567336149616], [1.1538463643119776], [-1.2382365752441329], [-0.7472736720455563], [-0.42787240873812343], [-0.11099743997050705], [0.06662416047563365], [0.43604990224783824], [-0.15124968957412893], [-0.04854570237278381], [0.2890362670645445], [0.5346135482924833]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-11/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-11/predictions.npz new file mode 100644 index 0000000..9fb8202 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-11/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-11/training.json b/benchmarks/v1/evidence/current-selection-046/trial-11/training.json new file mode 100644 index 0000000..dee2f15 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-11/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.6425981675106403, + "last_score": 0.6425981675106403, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "4ca1ace37ee4aba52b2cac232e166793eea333919e70e62a455cace0f859b2ce", + "best_validation_score": 0.6425981675106403, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-11/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-11/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-12/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-12/execution.json new file mode 100644 index 0000000..5ac5d35 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-12/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "cf757d5eba93ab8dfbeada6aa220f75c365a0174a6b51015dee0bf5245a58e59", + "predictions.npz": "9fc653a5dfddf376905feecb1065b56cad1e7db5af912604196f5f7eb08c8398", + "training.json": "c9996f5b9eba2a68ff41c1d64b7cbe0dbb66e87bb831f273e1db22a4f4a18da5", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-12.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.1899553749972256 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-12/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-12/model.bin new file mode 100644 index 0000000..7876685 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-12/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [8, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.00029829374431268406], [-0.7214664491165508], [0.7220539974008029]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [6, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-5.124106267500723e-17], [-0.8135343465107328], [0.6376350283462501]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [10, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.000269368290318744], [-0.45956934497644114], [0.9856252125147891]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [8, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.00027061433563765165], [-0.5870956552015063], [0.748446527953079]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [5, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.0010517481805766094], [-0.791976296963349], [0.4845797818492975]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [5, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[4.6792098515794445e-06], [-0.78769312901617], [0.4803081066840148]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-12/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-12/predictions.npz new file mode 100644 index 0000000..468657b Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-12/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-12/training.json b/benchmarks/v1/evidence/current-selection-046/trial-12/training.json new file mode 100644 index 0000000..5ec6099 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-12/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.5593656105789991, + "last_score": 0.5593656105789991, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "9fbef7b7b06987f983d0298f4d565df170d1d3d08cde990ec6fa3ef25fa3e543", + "best_validation_score": 0.5593656105789991, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-12/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-12/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-13/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-13/execution.json new file mode 100644 index 0000000..1eaaea8 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-13/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "745ce8414f74020f78a2f0583d44abd16944b36324f3864f94e72fe5a09c5670", + "predictions.npz": "600e5a681c7bce7b047ff68952614c0759acd1accb03db1d72d6752527915276", + "training.json": "ab084eca35b01d7b7e73468eb133d3a1ca02e9253450cf226c62e8bbf23165df", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-13.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.24297970900079235 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-13/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-13/model.bin new file mode 100644 index 0000000..2a2ec45 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-13/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916], [-1.1796216992510447], [-0.28343107662342826], [0.4395337249025689], [1.4256575517704677]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785], [-1.2080578843704168], [-0.3260402145908945], [0.4234637259088333], [1.3220714952505224]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.000618674616613171], [-0.6365233682246279], [0.8135027060134855], [-1.0685984804980053], [-0.2564376407545304], [0.39816584491173884], [1.2940583931455016]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [6, 3, 11, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.00032528788030467763], [-0.8096957056726982], [0.6351978155872446], [-1.0943583187826125], [-0.37516031116952553], [0.23959220490534525], [1.0865225219832826]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [7, 3, 11, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0011217119500360033], [-0.6544419823503199], [0.6566514149791788], [-0.9680245058628988], [-0.3023628716406636], [0.208610673088946], [1.066065603047107]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [8, 4, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[7.21377029456977e-05], [-0.5786076835324767], [0.7383852841859692], [-0.910129291461174], [-0.1350452452951165], [0.4798293016636715], [1.2596397673840005]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [9, 4, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0010161390606208678], [-0.46383019476247406], [0.7633234809680716], [-0.8133104419881312], [-0.09226279540527021], [0.39172897759549224], [1.076200793496953]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [7, 2, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0011330693669455527], [-0.5920445165222495], [0.5942763198207787], [-0.9986096936816236], [-0.3121687155891964], [0.3712828984614756], [1.1476717880609781]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-13/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-13/predictions.npz new file mode 100644 index 0000000..0be9353 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-13/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-13/training.json b/benchmarks/v1/evidence/current-selection-046/trial-13/training.json new file mode 100644 index 0000000..18ba40f --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-13/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.4599626585545453, + "last_score": 0.4599626585545453, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "40e58d36dc7ae2b284af276635756da431250f3262449f97925e43bda72fd8cf", + "best_validation_score": 0.4599626585545453, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-13/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-13/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-14/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-14/execution.json new file mode 100644 index 0000000..4741d81 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-14/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "fd9f8ba40952897066dbe8729b55d1ef8cd0a87e1451cce3f7c579298dc194d0", + "predictions.npz": "9c331ac5dc4710fe14bf273fd15447a89b374819c98a3c9999ef7ba2aed6485e", + "training.json": "5b256f0e82652846d71544640ba1f115aad6c4275799cbc26b07f853e8fedbd5", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-14.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.35101504198974 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-14/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-14/model.bin new file mode 100644 index 0000000..bd725ee --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-14/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916], [-1.1796216992510447], [-0.28343107662342826], [0.4395337249025689], [1.4256575517704677], [-0.4181223204361509], [-0.05738471593578133], [0.22998810011862172], [0.6002422691417862]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, 5, 9, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785], [-1.2080578843704168], [-0.3260402145908945], [0.4234637259088333], [1.3220714952505224], [-0.4903954777096544], [-0.12545826096203508], [0.18783091914567335], [0.554019997828803]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[0.0009242449006275415], [-0.6370424924401643], [0.8148499358216161], [-1.0685984804980053], [-0.2573522881819042], [0.40046406046678507], [1.2940583931455016], [-0.3795264139343523], [-0.052283852297045214], [0.2095447134414109], [0.5468874007736275]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 8, 13, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00036924736797366256], [-0.7167906689821899], [0.7175179744039563], [-1.0943583187826125], [-0.2970588621828149], [0.38464997673211404], [1.2000341264581667], [-1.302408627639202], [-0.7647126411723998], [-0.44680476857990736], [-0.1143064155431875], [0.060277527295297485], [0.4574271691116416], [0.6788935439827067], [1.3562195471158478]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 5, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, -1, -1, -1], "value": [[0.001678100042304982], [-0.5773934019710919], [0.7404355991613872], [-0.9680245058628988], [-0.23367329872672044], [0.3648672550919597], [1.1746068491628399], [-0.40322850880343825], [-0.09831405338693726], [0.19091851669106327], [0.49827518737152726]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, 13, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.00021630006897751776], [-0.6530759428504398], [0.6535019884408501], [-0.997082023779714], [-0.270653629988787], [0.3486502395179834], [1.0956854298977232], [-1.1866389718490506], [-0.6967381841792976], [-0.40708878915058233], [-0.10414584527268196], [0.14812573982574953], [0.4606556439573773], [0.6245820604640901], [1.235666698483328]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, 1, 5, 10, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0021168030921854862], [-0.5236258646606133], [0.6728189377046466], [-0.8769163170758024], [-0.21269426676924064], [0.3324346101948966], [1.0661816015478085], [-1.0417833645820418], [-0.6146141232278073], [-0.3673859746875771], [-0.08923890999737383], [0.17394798187407987], [0.45398405960516924]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [6, 3, 11, 1, 5, 8, 14, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0003091598348166217], [-0.6723803291407614], [0.5275439171444638], [-0.9084525105548502], [-0.31194898966535595], [0.19345699680395306], [0.9092075294977733], [-1.0811599521291353], [-0.6348059011411379], [-0.37090311900386386], [-0.14344175892297048], [0.009026577000769895], [0.3062582876827758], [0.6899412984830734], [1.2974582242364385]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-14/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-14/predictions.npz new file mode 100644 index 0000000..4f1f392 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-14/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-14/training.json b/benchmarks/v1/evidence/current-selection-046/trial-14/training.json new file mode 100644 index 0000000..f9acb80 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-14/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.44390312749311694, + "last_score": 0.44390312749311694, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "9292fea5228aeb1c0f2ff405be766ebf16c1361b72680fb1c32f846abee7770b", + "best_validation_score": 0.44390312749311694, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-14/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-14/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-15/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-15/execution.json new file mode 100644 index 0000000..4c29e60 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-15/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "ae7ac62b0c51cd7c13596703c9654245cd4d7e7d0522eacd8d30b1cd57517ce1", + "predictions.npz": "e0e4f009555742e828529b6e0eada543fcce53c7d09ba89d0d1c61f67814ff37", + "training.json": "885f3313546ff3fb8a2fe015c80c29e223f8a29635bff235f903f39359a059c2", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-15.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.4539316250011325 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-15/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-15/model.bin new file mode 100644 index 0000000..e1e7367 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-15/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916], [-1.1796216992510447], [-0.28343107662342826], [0.4395337249025689], [1.4256575517704677], [-0.4181223204361509], [-0.05738471593578133], [0.22998810011862172], [0.6002422691417862], [-0.4741306185252607], [-0.23368291978852299], [-0.11367372000521055], [0.010381231320804147], [0.2444359897228614], [0.057202491642352235]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, 1, 1, -1, -1, 1, 2, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, 5, 9, -1, -1, 6, 5, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, -1, 11, 13, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, -1, 12, 14, -1, -1, -1, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785], [-1.2080578843704168], [-0.3260402145908945], [0.4234637259088333], [1.3220714952505224], [-0.4903954777096544], [-0.12545826096203508], [0.18783091914567335], [0.554019997828803], [-0.17095460066520288], [-0.05487026906646029], [0.07530400082695135], [0.2627916536352607]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0005181701169665047], [-0.6379495167391542], [0.8150970061017767], [-1.0685984804980053], [-0.2589503786134578], [0.4008855332976473], [1.2940583931455016], [-0.3817548079180266], [-0.05279393866091883], [0.21034082878859525], [0.5468874007736275], [-0.43198567465634874], [-0.21498828620544114], [-0.10457982240479372], [0.009550732815139813], [0.223047840622111], [0.054342367060234625]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, 13, -1, -1, -1, 6, 8, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, 15, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, 16, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0004460137165073635], [-0.7170948102208857], [0.7179733220867337], [-1.0943583187826125], [-0.2976492539991069], [0.3853655230907643], [1.2000341264581667], [-1.302408627639202], [-0.7647126411723998], [-0.44680476857990736], [-0.1154216000850723], [0.1728044456140195], [0.5028796903369134], [0.6788935439827067], [1.3562195471158478], [-0.15727823261198665], [-0.05048064754114345], [0.06927968076079526], [0.24176832134443985]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, 0, 0, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, 6, 10, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, -1, 7, 9, -1, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, -1, 8, 10, -1, 12, 14, 16, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0009397907524268057], [-0.5790480827164532], [0.7408919123936731], [-0.9680245058628988], [-0.23658868861140464], [0.3656456717823295], [1.1746068491628399], [-0.3485560499020837], [-0.04857042356804533], [0.19238885932842845], [0.49827518737152726], [-0.3935869480202288], [-0.19778922330900583], [-0.0962134366124102], [0.00878667418992863], [0.2035311545676763], [0.05162524870722289]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 8, 13, -1, -1, -1, 6, -1, 10, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, 15, -1, 17, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, 16, -1, 18, -1, -1, -1, -1, -1, -1], "value": [[0.0005973444463890074], [-0.6536328592519629], [0.6548094467978807], [-0.997082023779714], [-0.27173470300350827], [0.3507048169361743], [1.0956854298977232], [-1.1866389718490506], [-0.6967381841792976], [-0.40708878915058233], [-0.10618787207826648], [0.06373730629993163], [0.41447733083294125], [0.6245820604640901], [1.235666698483328], [-0.1446959740030277], [-0.04644219573785198], [0.2717222847537718], [0.511179340152895]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [8, 3, 12, 1, 6, 10, -1, -1, -1, 5, 7, 12, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, -1, -1, -1, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, -1, -1, -1, 14, 16, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[0.001278443985175906], [-0.5255941594167394], [0.6734511295674411], [-0.8769163170758024], [-0.21616221467289135], [0.33351305513731083], [1.0661816015478085], [-1.0417833645820418], [-0.6146141232278073], [-0.31824949238363864], [-0.0446847896826017], [0.1759850445430845], [0.45398405960516924], [-0.35860144152954176], [-0.18196608544428539], [-0.0885163616834174], [0.008083740254734339], [0.1857221785430046], [0.04904398627186175]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 0, 1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 3, 12, 1, 5, 9, 14, -1, -1, -1, 10, 8, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, 15, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, 16, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0008842001174950837], [-0.5957895857962208], [0.5975311920882562], [-0.9084525105548502], [-0.2480802148142836], [0.3196659492006835], [1.000426492746008], [-1.0811599521291353], [-0.6348059011411379], [-0.37090311900386386], [-0.09769284231200517], [0.14407077480695554], [0.4166421507655195], [0.7242503649462123], [1.2974582242364385], [-0.04939588685386203], [-0.14571506492121858], [0.05863832179593711], [0.20068907285658288]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-15/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-15/predictions.npz new file mode 100644 index 0000000..c3f5ff8 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-15/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-15/training.json b/benchmarks/v1/evidence/current-selection-046/trial-15/training.json new file mode 100644 index 0000000..b3731d3 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-15/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.4417503988764809, + "last_score": 0.4417503988764809, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "937fa570ae815a0cb23002fdbb12c325a2510cb7137e31bbfd27534eee1de467", + "best_validation_score": 0.4417503988764809, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-15/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-15/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-2/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-2/execution.json new file mode 100644 index 0000000..13f0b4d --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-2/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "95fe4e465adead4d4b4cd27063d803820cfb351eed2fc26e07eda120af704267", + "predictions.npz": "9a7399c215bdc8b53dd84dca481a934ec37d9ff4c2208080f662fa98d7d46c55", + "training.json": "59b3ca7f5c5a144192498ae32dc0ac64b693f14fa762f475c69e0bdf015c666a", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-2.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.34371020800608676 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-2/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-2/model.bin new file mode 100644 index 0000000..2efa5f0 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-2/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, -1, 1, 1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 5, -1, 11, 12, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, -1, 9, 11, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, -1, 10, 12, -1, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0], [-0.26036447312296984, -0.6621493740935572, -0.0], [1.3837394915426051, -0.9863847343809521, -0.0], [-1.0431332214669604, 0.7388624736089101, -0.0], [0.5000301905763517, 0.7952356253524009, -0.0], [-0.7319421876729756, -0.741988705859847, -0.0], [0.03993978714856493, -0.5701024834275459, -0.0], [-1.0230654534118047, 0.27565331169011476, -0.0], [-0.9548741137643889, 1.0355435557830552, -0.0], [0.4531822926908389, 0.4302269445607008, -0.0], [0.5103876212513184, 1.3927137160437344, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 3, -1, 11, 12, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, -1, 9, 11, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, -1, 10, 12, -1, -1, -1, -1, -1, -1], "value": [[-0.00024817889401219055, 0.0006428166187763377, -0.0], [0.266839418285886, -0.7542608513457146, -0.0], [-0.26732825550136463, 0.7555270052917897, -0.0], [-0.24885060251143964, -0.6318942775395041, -0.0], [1.3208422419270318, -0.9415490646363636, -0.0], [-0.9967940297088366, 0.7057758113033855, -0.0], [0.4778627102061874, 0.7608354342630292, -0.0], [-0.2580187754796672, -1.0548064901407357, -0.0], [-0.22452686449760284, -0.28467882014294554, -0.0], [-0.9783063398250383, 0.2635934793036722, -0.0], [-0.9119047786449913, 0.9889440957728176, -0.0], [0.43241143760917544, 0.4105082096016687, -0.0], [0.4891214703658469, 1.3346839778752455, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, 1, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 7, 4, 3, 3, 12, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[-0.00033606349434640067, 0.0011619799321708347, -0.0], [0.25476759373818225, -0.7200039214677021, -0.0], [-0.2554295369846222, 0.7222926698189477, -0.0], [-0.23745708464668783, -0.603211488250102, -0.0], [1.2608039582030761, -0.8987513798801653, -0.0], [-0.8379795535222584, 0.6335385398459643, -0.0], [0.5950297392823309, 0.7974919505075704, -0.0], [-0.7891870943439867, -0.45521278606649107, -0.0], [0.003693924325534471, -0.6286691016051124, -0.0], [1.233170678125946, -1.1279023676506013, -0.0], [1.0008695288065665, -0.2157615593692517, -0.0], [-1.0989231853138788, 0.5852003776565892, -0.0], [-0.3091987076705932, 0.6328957359229532, -0.0], [0.6131075439276683, 0.38782827048036944, -0.0], [0.4687414091006033, 1.279072145463777, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [6, 10, 6, 4, 3, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0005440274472616165, 0.0014557661008178773, -0.0], [0.2647393044116186, -0.7833020848205012, -0.0], [-0.20654264983418746, 0.616496898820208, -0.0], [-0.29524544977009626, -0.6977171233524112, -0.0], [1.2079184885062517, -0.8599213741907925, -0.0], [-0.8708484826648197, 0.60618641277211, -0.0], [0.46863384877719155, 0.5943601796672422, -0.0], [-0.2073933784835481, -0.8873680862905039, -0.0], [-0.4162213750493863, -0.2458343174170378, -0.0], [1.179219460957936, -1.0785566390658874, -0.0], [0.9633369214763204, -0.20767050089290479, -0.0], [-0.8174001072434496, 0.23460049696384194, -0.0], [-0.8372120098197078, 0.9171536873031672, -0.0], [0.3383456601290421, 0.13777832925565464, -0.0], [0.5520586525476219, 0.9915060121121057, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-2/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-2/predictions.npz new file mode 100644 index 0000000..f07d981 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-2/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-2/training.json b/benchmarks/v1/evidence/current-selection-046/trial-2/training.json new file mode 100644 index 0000000..b060174 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-2/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.7091261650813435, + "last_score": 0.7091261650813435, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "cc7de4e2b85c7bfe096bbbb368c90938fe241211c9d7882d5268f41e4e5a9d2b", + "best_validation_score": 0.7091261650813435, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-2/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-2/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-3/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-3/execution.json new file mode 100644 index 0000000..27d383f --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-3/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "376fea27ad06665a57b0814406341151bc510c814963a1aad516328d3e16abea", + "predictions.npz": "dce5afafea701d16d5d6028355dd0dc0d3525f25a7efd9cd49378d6967df4eb3", + "training.json": "1050e95ead4482a23010ce3004f06991ac5dde5ac232ed939c2b863f475cc1ff", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-3.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.4640418749913806 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-3/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-3/model.bin new file mode 100644 index 0000000..5c69b7e --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-3/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, -1, 1, 1, 1, 1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 5, -1, 11, 12, 4, 5, -1, -1, 10, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, -1, 9, 11, 13, 15, -1, -1, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, -1, 10, 12, 14, 16, -1, -1, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0], [-0.26036447312296984, -0.6621493740935572, -0.0], [1.3837394915426051, -0.9863847343809521, -0.0], [-1.0431332214669604, 0.7388624736089101, -0.0], [0.5000301905763517, 0.7952356253524009, -0.0], [-0.7319421876729756, -0.741988705859847, -0.0], [0.03993978714856493, -0.5701024834275459, -0.0], [-1.0230654534118047, 0.27565331169011476, -0.0], [-0.9548741137643889, 1.0355435557830552, -0.0], [0.4531822926908389, 0.4302269445607008, -0.0], [0.5103876212513184, 1.3927137160437344, -0.0], [-0.5094210161542292, -0.9999874657815235, -0.0], [-0.8827383980328514, -0.16949338951237078, -0.0], [-0.022468241920229722, -0.7427028359394163, -0.0], [0.1692494936702002, -0.07636121121592207, -0.0], [0.03151343865686617, 0.3897263174745658, -0.0], [0.8695989069486673, 0.40577318540107504, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 1, -1, 1, 1, -1, 0, -1, -1, 0, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 3, -1, 11, 12, -1, 6, -1, -1, 10, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, -1, 9, 11, -1, 13, -1, -1, 15, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, -1, 10, 12, -1, 14, -1, -1, 16, -1, -1, -1, -1, -1], "value": [[-0.00032929097785682116, 0.00040344649966333595, -0.0], [0.2660009701617759, -0.7552857812141421, -0.0], [-0.266649573603009, 0.7560804485619639, -0.0], [-0.2500535932982063, -0.6333648290898559, -0.0], [1.3208422419270318, -0.9415490646363636, -0.0], [-0.9967940297088366, 0.7057758113033855, -0.0], [0.4791801515382896, 0.7619097653168966, -0.0], [-0.26090899818332225, -1.0453315061295054, -0.0], [-0.22443876171610883, -0.2938625719836879, -0.0], [-0.9783063398250383, 0.2635934793036722, -0.0], [-0.9119047786449913, 0.9889440957728176, -0.0], [0.43427781282965333, 0.41203017859464763, -0.0], [0.4891214703658469, 1.3346839778752455, -0.0], [-0.6543963453806778, -0.2691399229565763, -0.0], [0.1798289692049025, -0.2787620683844497, -0.0], [0.030162862714429035, 0.37302376101137014, -0.0], [0.8333656191591393, 0.3888659693426968, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, 1, 1, 1, -1, 1, -1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 4, 3, 11, 12, -1, 3, -1, -1, -1, 1, 10, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, 15, -1, -1, -1, 17, 19, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, 16, -1, -1, -1, 18, 20, -1, -1, -1, -1, -1, -1, -1], "value": [[-0.0002760535817814299, 0.0010096808832947006, -0.0], [0.25358783963485065, -0.7213620717929907, -0.0], [-0.25413158153835946, 0.7233508371691773, -0.0], [-0.23914977531668538, -0.6051601387168207, -0.0], [1.2608039582030761, -0.8987513798801653, -0.0], [-0.9525137726894831, 0.6741709547825591, -0.0], [0.45919952617384396, 0.7299806703105498, -0.0], [-0.7784007837335365, -0.4625313233953863, -0.0], [-0.0030376085970004935, -0.6282919956893632, -0.0], [1.233170678125946, -1.1279023676506013, -0.0], [1.0008695288065665, -0.2157615593692517, -0.0], [-0.9355054374576929, 0.2520612645841366, -0.0], [-0.8708690636059666, 0.9444416114630411, -0.0], [0.4161619575293105, 0.39460321020805716, -0.0], [0.4687414091006033, 1.279072145463777, -0.0], [-0.10562002344659331, -1.007744877172306, -0.0], [0.0793320841423738, -0.26190049093407247, -0.0], [-1.133659651569813, 0.9318524654770943, -0.0], [-0.3813465453281578, 0.7706602643535687, -0.0], [0.028870168598096362, 0.3570370283965971, -0.0], [0.7986420516941752, 0.37266322062008445, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, 1, 0, 1, 1, 1, -1, -1, 0, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 6, 6, 3, 3, 13, 4, 5, -1, -1, 1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, 15, 17, -1, -1, 19, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, 16, 18, -1, -1, 20, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[-5.064053428312467e-05, 0.0016418935855712217, -0.0], [0.24307413171961526, -0.689954332776859, -0.0], [-0.2431738782265366, 0.6931883655969231, -0.0], [-0.2289416098618028, -0.5786677332842445, -0.0], [1.2079184885062517, -0.8599213741907925, -0.0], [-0.873116373745878, 0.6387608000741544, -0.0], [0.4807097966218811, 0.7110951039602309, -0.0], [-0.5661186123408455, -0.5888451174146171, -0.0], [0.1273138601057234, -0.5202680380468513, -0.0], [1.179219460957936, -1.0785566390658874, -0.0], [0.9633369214763204, -0.20767050089290479, -0.0], [-1.0519353533136748, 0.560493873120149, -0.0], [-0.3401558557246722, 0.7018790084621398, -0.0], [0.45161141681478395, 0.4039361604322444, -0.0], [0.4544039488345383, 1.3060575476353524, -0.0], [-0.43135658623182793, -0.8661510727427565, -0.0], [-0.6289878740778915, -0.16718064996268509, -0.0], [0.044154860605235074, -0.6772878111093312, -0.0], [0.2825931439553913, -0.036906539144558645, -0.0], [-1.206891184374238, 0.8261769948469333, -0.0], [-0.746703043208301, 0.21474019809048625, -0.0], [0.01117791793288333, 0.2376485993029443, -0.0], [0.6704309265141238, 0.45737386608402636, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-3/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-3/predictions.npz new file mode 100644 index 0000000..1808bae Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-3/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-3/training.json b/benchmarks/v1/evidence/current-selection-046/trial-3/training.json new file mode 100644 index 0000000..8bab8df --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-3/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.6912022162278179, + "last_score": 0.6912022162278179, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "378d2e962a6fbd36bd67372009754a2fb0e08d1367236e939ec6282eb2d7c62c", + "best_validation_score": 0.6912022162278179, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-3/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-3/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-4/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-4/execution.json new file mode 100644 index 0000000..4add0bf --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-4/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "77f8826c52769c5fbed67a95e0ec5106b8de90abef018e1c2177538268e3af4d", + "predictions.npz": "0e5b1d72e070f0bf09d8b506e6a791a900e06045094f13730fa964e95981c860", + "training.json": "d8f051889b1b68518e825937cff19242e0a24ca4a17038286ccef753a67fd339", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-4.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.18671891700068954 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-4/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-4/model.bin new file mode 100644 index 0000000..f266ccd --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-4/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[6.832141690000964e-18, -5.124106267500723e-17, -0.0], [-0.7845351346489626, 0.11885608100569962, -0.0], [0.7845351346489625, -0.11885608100569964, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [6, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.416070845000482e-18, 1.878838964750265e-17, -0.0], [0.2713109546920187, -0.8118949522899644, -0.0], [-0.21264912665050115, 0.636350097740783, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [9, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[9.02489662177362e-05, -0.00027006900699875553, -0.0], [-0.5659004466841157, 0.06568983527152218, -0.0], [0.928311379874116, -0.1084335092634932, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-4/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-4/predictions.npz new file mode 100644 index 0000000..4e465e5 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-4/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-4/training.json b/benchmarks/v1/evidence/current-selection-046/trial-4/training.json new file mode 100644 index 0000000..d426e39 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-4/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.7032513326370281, + "last_score": 0.7032513326370281, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "631b20613f40e215e7997e7c2bb4ac090367d25977a41051fe7fa7ce694bfa1f", + "best_validation_score": 0.7032513326370281, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-4/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-4/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-5/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-5/execution.json new file mode 100644 index 0000000..a54b6dc --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-5/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "8c75d5c101dca71985bf2a8a6ab8e28efdf04d12c741828b249b55c1f9c029b6", + "predictions.npz": "1c6de7db35fafd31179c06dd01a7624f3df2e8b18db02f6a895ce7a2efca2e57", + "training.json": "7970cd42c8af1c6c643b2aabd94fea310b09600f529787c58911e12d759e6fec", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-5.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.24036341600003652 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-5/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-5/model.bin new file mode 100644 index 0000000..4d1d9fe --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-5/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [7, 11, 5, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0], [-0.26036447312296984, -0.6621493740935572, -0.0], [1.3837394915426051, -0.9863847343809521, -0.0], [-1.0431332214669604, 0.7388624736089101, -0.0], [0.5000301905763517, 0.7952356253524009, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [6, 10, 6, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0008927261346600199, -0.00017605539925106956, -0.0], [0.27759080268612285, -0.8147263010089875, -0.0], [-0.21600286700390972, 0.6382599764407921, -0.0], [-0.3045927177646572, -0.7243797571290058, -0.0], [1.257944992311459, -0.8967133948917746, -0.0], [-0.9063822066401892, 0.6262682096051194, -0.0], [0.4857450445799439, 0.616659112937476, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [7, 11, 7, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0016230152327118253, -0.0007402434431385816, -0.0], [0.23203183277040107, -0.6570782400628266, -0.0], [-0.22883498458475657, 0.6556201847960386, -0.0], [-0.21401736703005889, -0.5528890423111101, -0.0], [1.143586356646781, -0.8151939953561588, -0.0], [-0.7602288900285358, 0.5643949232897718, -0.0], [0.546644522090982, 0.7391119737481313, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, -1, -1, -1, -1], "threshold": [4, 10, 8, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.002562228512023959, -0.0007655670074853024, -0.0], [0.3299745932979013, -0.8852917022900071, -0.0], [-0.15028714679943042, 0.4120303087245245, -0.0], [-0.168988796080519, -0.8047741628013687, -0.0], [1.014035645366964, -0.9032290701858179, -0.0], [-0.6527416883254369, 0.36731072030002426, -0.0], [0.5716370515164437, 0.45389339181594496, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-5/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-5/predictions.npz new file mode 100644 index 0000000..168275b Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-5/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-5/training.json b/benchmarks/v1/evidence/current-selection-046/trial-5/training.json new file mode 100644 index 0000000..9ccf951 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-5/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.575128275308681, + "last_score": 0.575128275308681, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "94e8aa9657d0b2b1da7546ddf13723340234740e18f5df259ee72e95fe34f29e", + "best_validation_score": 0.575128275308681, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-5/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-5/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-6/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-6/execution.json new file mode 100644 index 0000000..3d2191a --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-6/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "8117fb27f9787110a39fc63644e5d25ed4192caa0f1f203505596046e4ac0756", + "predictions.npz": "7dbed58c84d6f3b217364a60144925c8c1e3c9a2cb966bae8af69616ba749ef2", + "training.json": "7192a11d4e71fbccba6cd6cc65d7d3daa5fb8acb5b63942c8847ffa544f3bb94", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-6.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.3485678330034716 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-6/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-6/model.bin new file mode 100644 index 0000000..b362d5f --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-6/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, -1, 1, 1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 5, -1, 11, 12, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, -1, 9, 11, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, -1, 10, 12, -1, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0], [-0.26036447312296984, -0.6621493740935572, -0.0], [1.3837394915426051, -0.9863847343809521, -0.0], [-1.0431332214669604, 0.7388624736089101, -0.0], [0.5000301905763517, 0.7952356253524009, -0.0], [-0.7319421876729756, -0.741988705859847, -0.0], [0.03993978714856493, -0.5701024834275459, -0.0], [-1.0230654534118047, 0.27565331169011476, -0.0], [-0.9548741137643889, 1.0355435557830552, -0.0], [0.4531822926908389, 0.4302269445607008, -0.0], [0.5103876212513184, 1.3927137160437344, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 1, 1, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [6, 10, 6, 3, 3, 1, 10, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[-0.0004963577880243743, 0.001285633237552708, -0.0], [0.27758286178565106, -0.8162905785088568, -0.0], [-0.2184369256217693, 0.6420538631675072, -0.0], [-0.3046048380864299, -0.7267673385761748, -0.0], [1.257944992311459, -0.8967133948917746, -0.0], [-0.9085736841837352, 0.6316218012178679, -0.0], [0.4831965132360791, 0.6186936165293829, -0.0], [-0.24437842640492186, -1.0257141539378503, -0.0], [-0.33437076595929516, -0.3551437893568819, -0.0], [1.2304189234552647, -1.1259408070992754, -0.0], [0.9985108819459826, -0.21408022175382962, -0.0], [-1.2522068837036884, 0.859507469557876, -0.0], [-0.6536501395050116, 0.46032784124879683, -0.0], [0.34298110736863013, 0.14612360840091293, -0.0], [0.5750922677799202, 1.0293942630049147, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, 1, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 7, 4, 3, 3, 12, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.0005723502445886546, 0.0008101071600507693, -0.0], [0.23336813713919263, -0.6574625331108833, -0.0], [-0.23224078059682113, 0.659058198729165, -0.0], [-0.21632173386218306, -0.5515129947976203, -0.0], [1.1524134913112336, -0.8192240647558073, -0.0], [-0.7662980872972158, 0.5795666229773492, -0.0], [0.5472868561606586, 0.7255420070368189, -0.0], [-0.7372689036477466, -0.4086651600400559, -0.0], [0.010910732159059796, -0.5778907505920516, -0.0], [1.1227572676529292, -1.0274209864780888, -0.0], [0.9236225658000339, -0.1980242051222924, -0.0], [-1.008929167047691, 0.5230640705164492, -0.0], [-0.276235321790542, 0.5989374428541427, -0.0], [0.5713811336952145, 0.33470664353082014, -0.0], [0.4199309638320486, 1.190871384456347, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [6, 10, 7, 4, 1, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[0.002093837174981041, 0.0013992171196230273, -0.0], [0.23108153092514583, -0.6842441202908585, -0.0], [-0.17743959406636386, 0.5387575297624431, -0.0], [-0.2585351131840672, -0.6110640383259139, -0.0], [1.0557755952115009, -0.7484420691129574, -0.0], [-0.6969854879858951, 0.5037743021057407, -0.0], [0.4747900157204904, 0.5502804857052846, -0.0], [-0.17399982692400684, -0.795916364187154, -0.0], [-0.3785956286407413, -0.1804719991084807, -0.0], [0.77623889177196, -1.0940265002410283, -0.0], [1.1046195840666726, -0.3946757512910555, -0.0], [-0.6208219185284811, 0.18923382252275747, -0.0], [-0.7186832225362025, 0.8308454473947465, -0.0], [0.4698403664674928, 0.26679833048757867, -0.0], [0.39270110063418606, 1.177277992130263, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-6/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-6/predictions.npz new file mode 100644 index 0000000..1404274 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-6/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-6/training.json b/benchmarks/v1/evidence/current-selection-046/trial-6/training.json new file mode 100644 index 0000000..be377b8 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-6/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.5519254024865582, + "last_score": 0.5519254024865582, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "c59c25bef05e8607c7e5a49472827249731f5d5a1780cc98c8df090eae0c1d37", + "best_validation_score": 0.5519254024865582, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-6/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-6/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-7/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-7/execution.json new file mode 100644 index 0000000..46869e8 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-7/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "a785681c8058f9d3fe1f36062580e98343f9cd4d42263e8c90366e0e205470dd", + "predictions.npz": "b92b6c179e8841c1fe9f14c4be9a17e9ec2614f1fb95f6a7f94ede9f93f68fdf", + "training.json": "026acdc310bc272e343936d8ab91bc0c4065727556aa563f5f7f9ba7619f01db", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-7.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.5652502919983817 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-7/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-7/model.bin new file mode 100644 index 0000000..bf788bd --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-7/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, -1, 1, 1, 1, 1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 5, -1, 11, 12, 4, 5, -1, -1, 10, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, -1, 9, 11, 13, 15, -1, -1, 17, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, -1, 10, 12, 14, 16, -1, -1, 18, -1, -1, -1, -1, -1, -1, -1], "value": [[1.708035422500241e-17, 2.9036602182504096e-17, -0.0], [0.2797803492466772, -0.7902929600709787, -0.0], [-0.27978034924667733, 0.7902929600709785, -0.0], [-0.26036447312296984, -0.6621493740935572, -0.0], [1.3837394915426051, -0.9863847343809521, -0.0], [-1.0431332214669604, 0.7388624736089101, -0.0], [0.5000301905763517, 0.7952356253524009, -0.0], [-0.7319421876729756, -0.741988705859847, -0.0], [0.03993978714856493, -0.5701024834275459, -0.0], [-1.0230654534118047, 0.27565331169011476, -0.0], [-0.9548741137643889, 1.0355435557830552, -0.0], [0.4531822926908389, 0.4302269445607008, -0.0], [0.5103876212513184, 1.3927137160437344, -0.0], [-0.5094210161542292, -0.9999874657815235, -0.0], [-0.8827383980328514, -0.16949338951237078, -0.0], [-0.022468241920229722, -0.7427028359394163, -0.0], [0.1692494936702002, -0.07636121121592207, -0.0], [0.03151343865686617, 0.3897263174745658, -0.0], [0.8695989069486673, 0.40577318540107504, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 1, 1, 1, 1, -1, 0, -1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 5, 3, 3, 11, 12, -1, 6, -1, -1, -1, 1, 10, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, 15, -1, -1, -1, 17, 19, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, 16, -1, -1, -1, 18, 20, -1, -1, -1, -1, -1, -1, -1], "value": [[-0.0006585819557136253, 0.0008068929993266446, -0.0], [0.25222159107687453, -0.7202786023573055, -0.0], [-0.25351879795934085, 0.721867937052949, -0.0], [-0.2397427134734429, -0.6045802840861547, -0.0], [1.257944992311459, -0.8967133948917746, -0.0], [-0.9504548379507125, 0.6726891489978613, -0.0], [0.45833011250022737, 0.7285839052813925, -0.0], [-0.25015887181223195, -1.0067641859153897, -0.0], [-0.2151781208404905, -0.27412176248769005, -0.0], [1.2304189234552647, -1.1259408070992754, -0.0], [0.9985108819459826, -0.21408022175382962, -0.0], [-0.9335472262382718, 0.2515336469172297, -0.0], [-0.8689354435255938, 0.9423446357625803, -0.0], [0.41537333296846785, 0.39383341262859434, -0.0], [0.46785531948037534, 1.2766542397067566, -0.0], [-0.6328902385342291, -0.25251469666033355, -0.0], [0.17721724724659194, -0.25876272477566575, -0.0], [-1.1318692626065048, 0.9299108213100012, -0.0], [-0.37962777192338193, 0.7687962859531592, -0.0], [0.028812286771991918, 0.3563212045481744, -0.0], [0.7971323313696117, 0.3719587532843187, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, 1, 0, 1, -1, 1, -1, -1, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1], "threshold": [7, 11, 7, 4, 3, 3, 13, -1, 3, -1, -1, 1, 6, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, 15, -1, -1, 17, 19, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, 16, -1, -1, 18, 20, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1], "value": [[-2.0641269134260052e-05, 0.002414068800186531, -0.0], [0.23161488027919125, -0.6569115379888614, -0.0], [-0.2316555373244558, 0.6616665219892288, -0.0], [-0.21883727631348934, -0.5507224365790669, -0.0], [1.1524134913112336, -0.8192240647558073, -0.0], [-0.7619289519765163, 0.5799664318609316, -0.0], [0.542424736273092, 0.7311190420304224, -0.0], [-0.7276805533035748, -0.41689993640438205, -0.0], [0.0035592069361628474, -0.5734303815581097, -0.0], [1.1227572676529292, -1.0274209864780888, -0.0], [0.9236225658000339, -0.1980242051222924, -0.0], [-1.003090438144243, 0.5346696261308296, -0.0], [-0.274800417956896, 0.581077937189731, -0.0], [0.5419573639799721, 0.3971832064672798, -0.0], [0.4348745336047135, 1.2527669047506234, -0.0], [-0.0880759955716083, -0.9296769573524996, -0.0], [0.07651144824876348, -0.23109008276678691, -0.0], [-1.158022690285534, 0.7894919302226298, -0.0], [-0.7048595519823461, 0.203465946877482, -0.0], [-0.3263774527774748, 0.6716587167336824, -0.0], [-0.08004620899677324, 0.20622373237191796, -0.0], [0.2840824059919894, 0.32472775017271566, -0.0], [0.8577491994639488, 0.424684453365947, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 0, 0, 0, 1, 1, 1, -1, 1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1], "threshold": [6, 10, 5, 2, 1, 12, 11, -1, 3, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], "left": [1, 3, 5, 7, 9, 11, 13, -1, 15, -1, -1, -1, -1, 17, -1, -1, -1, -1, -1], "right": [2, 4, 6, 8, 10, 12, 14, -1, 16, -1, -1, -1, -1, 18, -1, -1, -1, -1, -1], "value": [[0.0009268612698485163, 0.0038186730992001276, -0.0], [0.22912769475916253, -0.6817237088839484, -0.0], [-0.1779583017696097, 0.5410324678130409, -0.0], [-0.26151728417425235, -0.6072170945995775, -0.0], [1.0557755952115009, -0.7484420691129574, -0.0], [-0.791163975100922, 0.5373582789564213, -0.0], [0.38282471931705175, 0.5172876143933464, -0.0], [-0.7689763486995753, -0.3403698850488348, -0.0], [-0.1183076877820308, -0.6359778285747896, -0.0], [0.77623889177196, -1.0940265002410283, -0.0], [1.1046195840666726, -0.3946757512910555, -0.0], [-0.7697906035267912, 0.2442345426989495, -0.0], [-0.7216568641277366, 0.8732336314408925, -0.0], [0.3064179663574031, 0.19769356681038183, -0.0], [0.4591326029618493, 0.9719669899164954, -0.0], [-0.12761593719297556, -0.8379934471741382, -0.0], [-0.09304744622196415, -0.32921302907867356, -0.0], [-0.07119285618465479, 0.29661150331021047, -0.0], [0.4821553159521683, 0.12077320577599011, -0.0]]}, "mapping": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-7/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-7/predictions.npz new file mode 100644 index 0000000..726d7e2 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-7/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-7/training.json b/benchmarks/v1/evidence/current-selection-046/trial-7/training.json new file mode 100644 index 0000000..a0fbc07 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-7/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.5295721765671246, + "last_score": 0.5295721765671246, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "4790a1d5dbf8b43212c634c08661577f6bf078b62dad396e3d1fb212d44c4e78", + "best_validation_score": 0.5295721765671246, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-7/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-7/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-8/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-8/execution.json new file mode 100644 index 0000000..2f3448c --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-8/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "86e92ad30a93021cd662f196f2fb9bfae66b7bf8c10f1875fee796fb617e92dd", + "predictions.npz": "5353337dadadcb3735d9702aad54e246e805570e841aad1a474b532ab99e1912", + "training.json": "3cb01852d95d64b4a64bb8bac8f73779a9544571b95547cb5275b8e06443bc01", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-8.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.189344667000114 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-8/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-8/model.bin new file mode 100644 index 0000000..a349242 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-8/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [8, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [7, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.00014914687215633005], [-0.755544249603181], [0.7558380237453071]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [6, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-1.878838964750265e-17], [-0.8516864204451935], [0.6675380052138005]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [9, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.00014191550865782883], [-0.5666403494160861], [0.9296591533648915]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [8, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.00014165262710109222], [-0.6397673445289302], [0.8159373905796151]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, -1, -1], "threshold": [10, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.00041406496703174045], [-0.47392381392188687], [1.0168326594924746]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, -1, -1], "threshold": [5, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[9.454242929751722e-07], [-0.888497890545194], [0.5417685052733877]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-8/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-8/predictions.npz new file mode 100644 index 0000000..7bb72fc Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-8/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-8/training.json b/benchmarks/v1/evidence/current-selection-046/trial-8/training.json new file mode 100644 index 0000000..5bac54b --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-8/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.7131207372559492, + "last_score": 0.7131207372559492, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "7437093e9c3313075d9995753d0f67b07d3f8b7d9509ab84a0dd37d2eb50178b", + "best_validation_score": 0.7131207372559492, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-8/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-8/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/trial-9/execution.json b/benchmarks/v1/evidence/current-selection-046/trial-9/execution.json new file mode 100644 index 0000000..969da30 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-9/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "144837267fe6f77cacc0ccacaa12b87095388300d7e166a3f60fb309074e7baf", + "predictions.npz": "d1377727a4cc51b7d278a4e2de52166e8fd411cd10e7e701d3f9bbfaa685a169", + "training.json": "5056d916abf4a3b3a084568ab62c28397e02cf57783852f3094ec30a9ce065bc", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-selection-046/job-9.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 60, + "wall_s": 0.24095179200230632 +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-9/model.bin b/benchmarks/v1/evidence/current-selection-046/trial-9/model.bin new file mode 100644 index 0000000..cbaf1a6 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-9/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2"], "base": [1.592476150946709e-15, -1.215289500977601e-11, 0.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.708035422500241e-17], [-0.70285463503675], [0.8967455688399916], [-1.1796216992510447], [-0.28343107662342826], [0.4395337249025689], [1.4256575517704677]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[2.9036602182504096e-17], [-0.7902929600709787], [0.7902929600709785], [-1.2080578843704168], [-0.3260402145908945], [0.4234637259088333], [1.3220714952505224]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.00030933730830657355], [-0.6696890016306888], [0.8551241374267384], [-1.1241100898745249], [-0.2699343586889793], [0.41884978490715385], [1.3598579724579845]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [7, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.00016264394015231233], [-0.7531027031264617], [0.7534230624025195], [-1.1512081015765148], [-0.31069714566897], [0.40329878657984125], [1.2610528108543444]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [8, 3, 12, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0005897652958155757], [-0.6380883953985645], [0.8154346680681], [-1.0712107915274884], [-0.25708034160855175], [0.39913920679387604], [1.2970952968060776]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [6, 3, 11, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0003102397048283591], [-0.8115814183481559], [0.6366493706191989], [-1.0970336026787963], [-0.37586845281207587], [0.2402142686043361], [1.088913357189371]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [7, 3, 11, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0008433103929201278], [-0.6881580899061988], [0.6898191558316475], [-1.0208008719261947], [-0.31503542024466163], [0.2201911155741236], [1.1188695986873096]]}, "mapping": [[1.0, 0.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [1, 1, 1, -1, -1, -1, -1], "threshold": [8, 3, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0001841320044352591], [-0.6071523042527374], [0.7750553047461921], [-1.0454084919645001], [-0.22346147114070392], [0.5086564658435343], [1.31053533943615]]}, "mapping": [[0.0, 1.0, 0.0]], "coefficient": 0.05}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2"], "cuts": [[-1.125170637370958, -1.0090940451785873, -0.9072193073179762, -0.643954270202446, -0.46057373107824373, -0.3093061207204555, -0.16737741529596678, -0.05693813778562549, 0.0894501864930238, 0.24744007909328739, 0.35276316573110267, 0.546350201632099, 0.8631363229112268, 1.1001943158570067, 1.5442744142191622], [-1.4011934514788271, -1.066416299935658, -0.867597812118811, -0.6821560673237701, -0.3951269360006565, -0.2628254888584405, 0.02815441695248281, 0.1916862902807761, 0.4019346407090639, 0.5790411735158822, 0.7093623688771766, 0.9490589074746332, 1.0653033428093621, 1.3128855924515077, 1.7121347512979834], [-2.127445064682661, -1.4649717936460833, -1.0868920689698585, -0.7054083966268712, -0.42928538544403516, -0.24880851448226515, -0.11639472346743694, 0.04680498033738014, 0.20731314266479686, 0.3046500917127895, 0.3439119055267644, 0.6576782317122123, 0.8200782101637845, 0.9582494286813628, 1.406197440675289]], "categories": [null, null, null], "feature": [-1], "threshold": [-1], "missing_left": [false], "left": [-1], "right": [-1], "value": [[-0.0]]}, "mapping": [[0.0, 0.0, 1.0]], "coefficient": 0.05}], "classes": null}, "target_scale": {"mean": [100.10923680256714, -299.9982468963554, 7.0], "std": [17.47094347801772, 0.010889442180726704, 1.0], "constant": [false, false, true]}} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-9/predictions.npz b/benchmarks/v1/evidence/current-selection-046/trial-9/predictions.npz new file mode 100644 index 0000000..264be19 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/trial-9/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/trial-9/training.json b/benchmarks/v1/evidence/current-selection-046/trial-9/training.json new file mode 100644 index 0000000..0d0a9c8 --- /dev/null +++ b/benchmarks/v1/evidence/current-selection-046/trial-9/training.json @@ -0,0 +1,36 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 2, + "min_delta": 0.0, + "reference_score": 0.6519151910214706, + "last_score": 0.6519151910214706, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "49bd5b7a95904a1c7601051c1587cdc6d024489dd6e963185878fd585771c399", + "best_validation_score": 0.6519151910214706, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 100.10923680256714, + -299.9982468963554, + 7.0 + ], + "std": [ + 17.47094347801772, + 0.010889442180726704, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/current-selection-046/trial-9/worker.log b/benchmarks/v1/evidence/current-selection-046/trial-9/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-selection-046/validation.npz b/benchmarks/v1/evidence/current-selection-046/validation.npz new file mode 100644 index 0000000..82a9002 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/validation.npz differ diff --git a/benchmarks/v1/evidence/current-selection-046/worker-input.npz b/benchmarks/v1/evidence/current-selection-046/worker-input.npz new file mode 100644 index 0000000..446a343 Binary files /dev/null and b/benchmarks/v1/evidence/current-selection-046/worker-input.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/0/execution.json b/benchmarks/v1/evidence/current-worker-044/A1/0/execution.json new file mode 100644 index 0000000..b708470 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/0/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "42482c71af73600b32504cd2300db1cb3fa6b57f72fae08e0478a16dc496ff3c", + "predictions.npz": "772ef83d58e58e04a32a3ede96621ef5aa1b4e084d780d459e50f5159aa594a3", + "training.json": "1e6e4718642f9d2f10e36bdcf630cde271c5b2a2bc2e678c80306a8817279291", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/0/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.530277166995802 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/0/model.bin b/benchmarks/v1/evidence/current-worker-044/A1/0/model.bin new file mode 100644 index 0000000..582e9a9 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/0/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A1", "output": "mean", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.074558856536078], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 11, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-4.104911808125569e-16], [-0.33825563579920803], [1.2094482079428366], [-0.7229614854636287], [0.016718927721498054], [0.8202262738838512], [2.178682466430557]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [22, 9, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.8511636513831615e-05], [-0.36074463453027905], [0.9221759253060495], [-0.7119169463880707], [-0.09057798167191895], [0.6246178882462796], [1.9610953401057594]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [26, 15, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.1058682796443415e-05], [-0.2263318637428007], [1.2218763625511313], [-0.5013703830766084], [0.17374795826232892], [0.9827278418007996], [2.176549051957933]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [27, 12, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[5.081639521020047e-05], [-0.18003747130459244], [1.2599712314929445], [-0.5183113384357869], [0.11317199078212278], [1.0261270403099356], [1.9594565625378375]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/0/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A1/0/predictions.npz new file mode 100644 index 0000000..175abf3 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/0/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/0/replay.npz b/benchmarks/v1/evidence/current-worker-044/A1/0/replay.npz new file mode 100644 index 0000000..175abf3 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/0/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/0/training.json b/benchmarks/v1/evidence/current-worker-044/A1/0/training.json new file mode 100644 index 0000000..49af4a5 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/0/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4909141515050642, + "last_score": 0.4909141515050642, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "ab5a940ba2c8d3c29d6573cfbfc967b5ddd8bd78b53e00faa0c95af4dd43fccd", + "best_validation_score": 0.4909141515050642, + "output": "mean" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/0/worker.log b/benchmarks/v1/evidence/current-worker-044/A1/0/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A1/1/execution.json b/benchmarks/v1/evidence/current-worker-044/A1/1/execution.json new file mode 100644 index 0000000..3a6d44c --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/1/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "66abe5785919193ef683879004ac629a2a96db4d0e97e98560bdd1b1fffaf108", + "predictions.npz": "ca52ffc4433012145f3e58d40fa62064a199f1d95d0cc2c0fe631ed139656680", + "training.json": "f55ee5edc1ad857690013c7b09ad8589daeefc95326b7e2f0abc730f7b36bb86", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/1/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.4940680830040947 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/1/model.bin b/benchmarks/v1/evidence/current-worker-044/A1/1/model.bin new file mode 100644 index 0000000..0cad4f8 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/1/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A1", "output": "mean", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0628975587331473], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 10, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-9.73446416553048e-16], [-0.3370758353737078], [1.203522429179106], [-0.7353181633619749], [-0.024093198649269745], [0.8357429476190354], [2.12070636965816]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [25, 12, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.7739507109182226e-05], [-0.27548858607967985], [1.1934831448624355], [-0.6111938802534612], [0.06027145542840555], [0.9631888345422426], [2.339504540828701]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 5, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.818413492407045e-05], [-0.3251325363939553], [0.8308848305079114], [0.2297842320193302], [-0.47513855003646854], [0.4778656367118259], [1.5359043678209765]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [19, 3, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.864441087014254e-05], [-0.3677310143622507], [0.6131731210639321], [0.24670209386876318], [-0.4745210453092927], [0.35635187939046775], [1.3824461086953395]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/1/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A1/1/predictions.npz new file mode 100644 index 0000000..0fa5cc8 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/1/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/1/replay.npz b/benchmarks/v1/evidence/current-worker-044/A1/1/replay.npz new file mode 100644 index 0000000..0fa5cc8 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/1/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/1/training.json b/benchmarks/v1/evidence/current-worker-044/A1/1/training.json new file mode 100644 index 0000000..5b6ff5d --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/1/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.5102077406604998, + "last_score": 0.5102077406604998, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "87b5e02b84cb16887c94ab0341c7194069f6a861f29eb707da2e204df515c797", + "best_validation_score": 0.5102077406604998, + "output": "mean" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/1/worker.log b/benchmarks/v1/evidence/current-worker-044/A1/1/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A1/2/execution.json b/benchmarks/v1/evidence/current-worker-044/A1/2/execution.json new file mode 100644 index 0000000..1928eb0 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/2/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "e99803d4a6a65fc6f8772c8e2b33f2c22b742c303f4a37efc6138e0ec5e6b528", + "predictions.npz": "6917f874b654058be1e40a8ae8b3e7c134d7b5d2658057e27a857a7f3c4d3a5d", + "training.json": "83e73d9178b13588ec36e1e9b7d3290b9667393ff2e235ac739a5b3b8a838aca", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/2/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.5269187920057448 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/2/model.bin b/benchmarks/v1/evidence/current-worker-044/A1/2/model.bin new file mode 100644 index 0000000..393a7bd --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/2/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A1", "output": "mean", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0647951070741497], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 10, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[6.775542760861351e-16], [-0.34311125536944254], [1.2250717737840986], [-0.7554437811465498], [-0.019056078027826422], [0.7255912667466353], [1.8898482226887807]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [22, 9, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.48642683103932e-05], [-0.36662528472578676], [0.9368204301353207], [-0.7113045938985374], [-0.10115618140347075], [0.5541498590104594], [1.701026037960593]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 12, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[2.502673224696873e-05], [-0.27951944222276814], [0.9981328704894045], [-0.5712118476094475], [0.03666647417548576], [0.6883671771494072], [1.7706596439548727]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 5, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.8062867642770126e-05], [-0.299276809636563], [0.7648201974742622], [0.25176284458681714], [-0.4476393174806243], [0.5274617713569872], [1.593822151771531]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/2/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A1/2/predictions.npz new file mode 100644 index 0000000..a15d2d7 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/2/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/2/replay.npz b/benchmarks/v1/evidence/current-worker-044/A1/2/replay.npz new file mode 100644 index 0000000..a15d2d7 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/2/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/2/training.json b/benchmarks/v1/evidence/current-worker-044/A1/2/training.json new file mode 100644 index 0000000..6f64ddd --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/2/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.5128165758282013, + "last_score": 0.5128165758282013, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "6cf9181f4d2549e39573ee13a09a3c3adeafdaca25dddcb0b26cd2112eea6022", + "best_validation_score": 0.5128165758282013, + "output": "mean" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/2/worker.log b/benchmarks/v1/evidence/current-worker-044/A1/2/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A1/3/execution.json b/benchmarks/v1/evidence/current-worker-044/A1/3/execution.json new file mode 100644 index 0000000..20b464a --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/3/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "1266d5f96d6943d23fe6e504d7c1084335c355eef80f045592a14c1ae58122da", + "predictions.npz": "afc87a92eaccbd2d607a4e50a704c8acb4e442a402f4e9f6c8c3d9746e9dfa64", + "training.json": "cde5a96cb1e8451f8336aca661b93b07a47c691823aeb85bd9a9e98590261aa8", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/3/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.4972040409920737 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/3/model.bin b/benchmarks/v1/evidence/current-worker-044/A1/3/model.bin new file mode 100644 index 0000000..8c2e35f --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/3/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A1", "output": "mean", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0683685283475013], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 11, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.9800247208656e-16], [-0.34067098292810893], [1.2198137783691052], [-0.7208156642258853], [0.01003238421351284], [0.8329913160322193], [2.181552537765002]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [25, 11, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.8601215774053614e-05], [-0.2798742051769946], [1.2124857954170114], [-0.648749612561438], [0.03649253843930644], [0.8358640751510084], [1.9636787746384972]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 4, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.440185944155949e-05], [-0.3294013862169382], [0.841778762092968], [0.29541117311254594], [-0.4671850449192886], [0.4827545405804033], [1.558793811844895]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [19, 3, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.605876256491497e-05], [-0.36874152170345426], [0.6146390866051474], [0.24352781753574973], [-0.47615339759758235], [0.3515086422136792], [1.4030485781485347]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/3/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A1/3/predictions.npz new file mode 100644 index 0000000..2f3ffb3 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/3/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/3/replay.npz b/benchmarks/v1/evidence/current-worker-044/A1/3/replay.npz new file mode 100644 index 0000000..2f3ffb3 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/3/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/3/training.json b/benchmarks/v1/evidence/current-worker-044/A1/3/training.json new file mode 100644 index 0000000..aa235ac --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/3/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4828317821177374, + "last_score": 0.4828317821177374, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "b45a3dc822269c25aac6a2a3ee132a16b72bef07c5fcd213fcf91b1a0b2d114b", + "best_validation_score": 0.4828317821177374, + "output": "mean" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/3/worker.log b/benchmarks/v1/evidence/current-worker-044/A1/3/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A1/4/execution.json b/benchmarks/v1/evidence/current-worker-044/A1/4/execution.json new file mode 100644 index 0000000..99732c8 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/4/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "8dc0b4ed0f2c150c74470075e8ceb94298361343d5b025b799bdd29da8365981", + "predictions.npz": "b66b8e66b9b03c6503ddcf18107792d7c64eb8197fe8d695006b49606979b4c4", + "training.json": "f0615247ee36b25908a15bc92faafbbe911f78d6d54a61d438a0385a0c3a703f", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/4/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.5469587090046844 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/4/model.bin b/benchmarks/v1/evidence/current-worker-044/A1/4/model.bin new file mode 100644 index 0000000..aa4fa55 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/4/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A1", "output": "mean", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.069967600874343], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 10, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-3.3392926875185005e-15], [-0.34436226287684374], [1.2295384706995975], [-0.7564201238944978], [-0.020522673565858567], [0.8509973210827567], [2.173572183199696]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [26, 12, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.8147975024008937e-05], [-0.2533693636374006], [1.367734820600123], [-0.6299437882703293], [0.09662676391760631], [0.9738818052338075], [1.9564954258066176]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 5, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.568772097885164e-05], [-0.3337477284105968], [0.8528887114625858], [0.24138525698033836], [-0.4912052740265355], [0.4920115860841442], [1.5735984133065675]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [19, 4, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.678015180323546e-05], [-0.3759414444221276], [0.6266398478905901], [0.16547069099795714], [-0.4996994107284268], [0.3630641541258503], [1.416373993526111]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/4/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A1/4/predictions.npz new file mode 100644 index 0000000..7cedefa Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/4/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/4/replay.npz b/benchmarks/v1/evidence/current-worker-044/A1/4/replay.npz new file mode 100644 index 0000000..7cedefa Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A1/4/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A1/4/training.json b/benchmarks/v1/evidence/current-worker-044/A1/4/training.json new file mode 100644 index 0000000..fe3e9a0 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A1/4/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.504057415396101, + "last_score": 0.504057415396101, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "b2380388e213d2888ee48c373475e2843e7fe21f11ccb0db504f81c99845f731", + "best_validation_score": 0.504057415396101, + "output": "mean" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A1/4/worker.log b/benchmarks/v1/evidence/current-worker-044/A1/4/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A11/0/execution.json b/benchmarks/v1/evidence/current-worker-044/A11/0/execution.json new file mode 100644 index 0000000..b459279 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/0/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "0408cb3749259fced07bfe06f6a226d5abcd1340e70c8df0aa25a54d3bce4933", + "predictions.npz": "4246f7a6c7c45c369c2a1e9963b0ff399c7cce50268bedc633f4cd75f14beb3c", + "training.json": "60278e98745d17c90171b138595bf91f1caf15650ee5a25d4d6c5de28e57b23b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/0/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 3.0241687500092667 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/0/model.bin b/benchmarks/v1/evidence/current-worker-044/A11/0/model.bin new file mode 100644 index 0000000..352337c --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/0/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A11", "output": "normal_mean_scale", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0745588565360773, 0.14991480659031892], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 11, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.6007620874560587e-16], [-0.33825563579920803], [1.2094482079428366], [-0.7229614854636287], [0.016718927721498175], [0.8202262738838512], [2.178682466430557]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [29, 2, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.776468997385105e-16], [-0.10292091864261145], [1.5419545630443483], [0.30469516476715186], [-0.1469398580988348], [0.956671991143763], [2.1262774723629176]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [22, 9, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.8511636514328606e-05], [-0.36074463453027855], [0.9221759253060495], [-0.7119169463880696], [-0.09057798167191768], [0.6246178882462796], [1.9610953401057594]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [28, 5, 15, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.06864463349049321], [-0.13634738391764345], [0.5853694073251919], [0.11711905278206292], [-0.1986789719232641], [0.29125192551442713], [1.0708712450089293]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [26, 15, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.1058682797381135e-05], [-0.2263318637428007], [1.2218763625511313], [-0.5013703830766084], [0.17374795826232906], [0.9827278418007996], [2.176549051957933]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [27, 2, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.10446390788847316], [-0.1575199020701772], [0.2667899803329675], [0.1888321992878088], [-0.19678589754964554], [0.04377739969891514], [0.5570057949477246]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [27, 12, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[5.081639521128866e-05], [-0.1800374713045924], [1.2599712314929445], [-0.5183113384357869], [0.11317199078212288], [1.0261270403099356], [1.9594565625378375]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4124687723815441, 1.666700005531311, 1.8522625416517258, 2.0220999717712402, 2.1636688485741615, 2.301500082015991, 2.4375, 2.558600068092346, 2.6666998863220215, 2.7997812032699585, 2.935528077185154, 3.057362526655197, 3.16433747112751, 3.2729249745607376, 3.4028000831604004, 3.545799970626831, 3.6607000827789307, 3.785418853163719, 3.948499917984009, 4.082425117492676, 4.213200092315674, 4.382756143808365, 4.566199779510498, 4.75, 5.0, 5.199137479066849, 5.47291886806488, 5.827037513256073, 6.240525171160698, 6.868656158447266, 8.106399536132812], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.1567415073513985, 3.527465671300888, 3.741755433380604, 3.9088794887065887, 4.06152717769146, 4.202278256416321, 4.320426449179649, 4.434672713279724, 4.542843356728554, 4.6442055106163025, 4.7519021183252335, 4.8512808084487915, 4.941255405545235, 5.039104163646698, 5.12667179107666, 5.2198121547698975, 5.3190950602293015, 5.409476548433304, 5.498138815164566, 5.603309869766235, 5.707155212759972, 5.8104148209095, 5.934273287653923, 6.051661372184753, 6.1710386127233505, 6.328754425048828, 6.5023302137851715, 6.729376435279846, 7.010936588048935, 7.379601180553436, 8.07606628537178], [0.9212318751960993, 0.9469637684524059, 0.9629629850387573, 0.9741362631320953, 0.9836713671684265, 0.9917290918529034, 1.0, 1.0054984986782074, 1.0113636255264282, 1.0167666301131248, 1.0225564241409302, 1.02772256731987, 1.0336134433746338, 1.0384407043457031, 1.04347825050354, 1.0488744378089905, 1.053953193128109, 1.059195451438427, 1.0648868642747402, 1.0711242705583572, 1.0775861740112305, 1.0839432552456856, 1.0913223437964916, 1.0991916060447693, 1.1081081628799438, 1.1195929050445557, 1.1341463327407837, 1.1519938111305237, 1.177311822772026, 1.23100046813488, 1.3980230055749416], [261.0, 398.0, 494.0, 568.0, 631.0, 689.0, 737.0, 785.0, 829.71875, 874.0, 923.0, 967.625, 1015.0, 1063.0, 1114.0, 1161.0, 1211.0, 1269.0, 1330.0, 1391.0, 1466.0, 1538.0, 1621.0, 1707.25, 1818.0, 1950.0, 2099.0, 2313.0, 2625.09375, 3079.0625, 3906.0], [1.7675667777657509, 1.9355993196368217, 2.052563026547432, 2.1608937978744507, 2.2435469180345535, 2.308724746108055, 2.3764060512185097, 2.432608425617218, 2.481669381260872, 2.5344307124614716, 2.5887368246912956, 2.638631284236908, 2.680810399353504, 2.727783501148224, 2.7734669372439384, 2.8234530687332153, 2.8688215985894203, 2.922365814447403, 2.9741675555706024, 3.024710178375244, 3.0807576701045036, 3.1423234045505524, 3.205865330994129, 3.28384268283844, 3.3688959851861, 3.4784656763076782, 3.6025560572743416, 3.742356061935425, 3.9412450566887856, 4.218758374452591, 4.636996239423752], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.47999954223633, 35.369998931884766, 36.54999923706055, 36.84000015258789, 37.290000915527344, 37.38999938964844, 37.58000183105469, 37.720001220703125, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.2599983215332, 38.540000915527344, 38.70000076293945, 39.41999816894531], [-122.58000183105469, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.11000061035156, -122.01000213623047, -121.91000366210938, -121.77999877929688, -121.45999908447266, -121.30000305175781, -121.01000213623047, -120.47375202178955, -119.80406332015991, -119.3499984741211, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.35562467575073, -118.30999755859375, -118.26000213623047, -118.20999908447266, -118.1500015258789, -118.0999984741211, -118.0199966430664, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [5, 0, 0, -1, -1, -1, -1], "threshold": [10, 18, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.12698137505341733], [0.02838337627543354], [-0.20835639102083792], [-0.10818454130425473], [0.26680860995017236], [-0.22873265165585524], [0.3315611966315385]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/0/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A11/0/predictions.npz new file mode 100644 index 0000000..bfccca4 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/0/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/0/replay.npz b/benchmarks/v1/evidence/current-worker-044/A11/0/replay.npz new file mode 100644 index 0000000..bfccca4 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/0/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/0/training.json b/benchmarks/v1/evidence/current-worker-044/A11/0/training.json new file mode 100644 index 0000000..5e80c8b --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/0/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3881190556099985, + "last_score": 1.3881190556099985, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "8428770340ff7481c5556ef396f5a01a4f5cede7fa42560dcd74b96519f9d697", + "best_validation_score": 1.3881190556099985, + "output": "normal_mean_scale" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/0/worker.log b/benchmarks/v1/evidence/current-worker-044/A11/0/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A11/1/execution.json b/benchmarks/v1/evidence/current-worker-044/A11/1/execution.json new file mode 100644 index 0000000..5e10669 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/1/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "c1c64c505259fb2e08cc4cd9216b8abf5e9522057c50ecb7225692d1c83237e1", + "predictions.npz": "fed0a1dbe065d670a30cf469277d1972b311526e932ae6bb6c7e7e6511185964", + "training.json": "10c1263a6c6edd20136887586438b06ac1011693d7f9c674d898eb542fd85dce", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/1/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 2.8695160830102395 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/1/model.bin b/benchmarks/v1/evidence/current-worker-044/A11/1/model.bin new file mode 100644 index 0000000..2768091 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/1/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A11", "output": "normal_mean_scale", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0628975587331455, 0.1332251371419521], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 10, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.0763022823951881e-15], [-0.3370758353737077], [1.203522429179106], [-0.7353181633619749], [-0.0240931986492693], [0.8357429476190356], [2.12070636965816]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [29, 2, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.337354949252176e-16], [-0.10191210923712418], [1.5268406456158092], [0.32238975348260485], [-0.14777832723046536], [0.8849759470315187], [2.1647701878969623]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [25, 12, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.7739507110517255e-05], [-0.2754885860796797], [1.1934831448624357], [-0.611193880253461], [0.06027145542840578], [0.963188834542243], [2.339504540828701]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [28, 2, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.06892873626568355], [-0.13824089651769628], [0.600631173721276], [0.235460508923194], [-0.17940053698497282], [0.26743895907024634], [1.0474831414602177]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 5, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.8184134925423663e-05], [-0.3251325363939552], [0.8308848305079152], [0.22978423201933124], [-0.4751385500364685], [0.4778656367118331], [1.5359043678209865]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [27, 5, 14, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.1047884711033074], [-0.1589077331918479], [0.2739043834639102], [0.07116520027244252], [-0.21675299200847947], [0.060723453313292174], [0.5923764925242304]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [19, 3, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.8644410871491713e-05], [-0.3677310143622505], [0.6131731210639406], [0.24670209386876527], [-0.4745210453092924], [0.35635187939047475], [1.3824461086953395]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4375, 1.6828874573111534, 1.875, 2.046062409877777, 2.183799982070923, 2.3085125535726547, 2.4471813067793846, 2.5675999522209167, 2.679390572011471, 2.802775055170059, 2.933662459254265, 3.0467875599861145, 3.152178034186363, 3.2545562386512756, 3.3895125165581703, 3.5270999670028687, 3.646699905395508, 3.76708747446537, 3.9166998863220215, 4.0524001121521, 4.188540756702423, 4.355862468481064, 4.551309511065483, 4.7356250286102295, 4.965021967887878, 5.191012412309647, 5.4530282616615295, 5.765524923801422, 6.195365726947784, 6.816418528556824, 8.026080876588821], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0], [3.201537825167179, 3.5549146682024, 3.76095050573349, 3.933142900466919, 4.088773399591446, 4.231378078460693, 4.346409514546394, 4.455593466758728, 4.559761673212051, 4.655539035797119, 4.763299912214279, 4.859709560871124, 4.950316801667213, 5.046355187892914, 5.132518485188484, 5.22247576713562, 5.316779926419258, 5.404134511947632, 5.490326210856438, 5.59001362323761, 5.698380291461945, 5.8046199679374695, 5.926582887768745, 6.0481449365615845, 6.170516833662987, 6.327493965625763, 6.495545104146004, 6.718381583690643, 7.02484904229641, 7.389008581638336, 8.093984097242355], [0.9221294578164816, 0.949144221842289, 0.9651559162884951, 0.9765263423323631, 0.98591548204422, 0.9937864132225513, 1.0, 1.0070650279521942, 1.0126686915755272, 1.018205814063549, 1.0239176601171494, 1.028930276632309, 1.0339120142161846, 1.0385969951748848, 1.043874230235815, 1.0492682456970215, 1.0542551688849926, 1.0596828013658524, 1.0655981078743935, 1.0712562054395676, 1.0773481130599976, 1.0839415788650513, 1.0913606956601143, 1.100000023841858, 1.1089529432356358, 1.120508387684822, 1.1340844109654427, 1.1517552733421326, 1.1790206097066402, 1.235203467309475, 1.4148590229451656], [250.9375, 395.0, 495.90625, 572.0, 635.0, 693.0, 744.0, 790.0, 834.0, 878.0, 921.0, 970.0, 1015.59375, 1063.0, 1113.0, 1163.0, 1218.0, 1275.0, 1332.40625, 1392.0, 1471.0, 1544.3125, 1629.0, 1719.0, 1824.0, 1950.0, 2105.15625, 2316.125, 2626.28125, 3038.1875, 3844.40625], [1.7539608366787434, 1.9225076362490654, 2.0510566607117653, 2.151239722967148, 2.235173597931862, 2.3059809654951096, 2.370676353573799, 2.4265291690826416, 2.477828271687031, 2.5295114517211914, 2.5825796872377396, 2.6299238204956055, 2.673353224992752, 2.7152392864227295, 2.7600357085466385, 2.8101837635040283, 2.8568360209465027, 2.9101859778165817, 2.9632932245731354, 3.016641855239868, 3.071272298693657, 3.1303033977746964, 3.1989656314253807, 3.276505768299103, 3.3559024706482887, 3.455211266875267, 3.5746580362319946, 3.7173611223697662, 3.922792613506317, 4.192634850740433, 4.614964976906776], [32.7599983215332, 33.0, 33.609062910079956, 33.7400016784668, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2400016784668, 34.45000076293945, 35.369998931884766, 36.529998779296875, 36.849998474121094, 37.29999923706055, 37.400001525878906, 37.599998474121094, 37.720001220703125, 37.779998779296875, 37.869998931884766, 37.97999954223633, 38.2599983215332, 38.529998779296875, 38.70000076293945, 39.400001525878906], [-122.62000274658203, -122.44000244140625, -122.30999755859375, -122.2300033569336, -122.12000274658203, -122.0199966430664, -121.91000366210938, -121.78249931335449, -121.45999908447266, -121.30000305175781, -121.00343823432922, -120.4800033569336, -119.80999755859375, -119.33999633789062, -118.95999908447266, -118.4800033569336, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.94000244140625, -117.86000061035156, -117.68000030517578, -117.37000274658203, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [5, 0, 0, -1, -1, -1, -1], "threshold": [6, 18, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.12858652986337524], [0.0749191325614886], [-0.18556996916076265], [-0.09662725925453172], [0.45170687881229593], [-0.21460712286908065], [0.19587186458283035]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/1/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A11/1/predictions.npz new file mode 100644 index 0000000..3ba67fc Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/1/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/1/replay.npz b/benchmarks/v1/evidence/current-worker-044/A11/1/replay.npz new file mode 100644 index 0000000..3ba67fc Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/1/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/1/training.json b/benchmarks/v1/evidence/current-worker-044/A11/1/training.json new file mode 100644 index 0000000..1a8e1bd --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/1/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.394201272104827, + "last_score": 1.394201272104827, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "7b540ad7b9493b90c52e31501a9a3107f41d0c01ebcee1bc4394efc2fd84cb22", + "best_validation_score": 1.394201272104827, + "output": "normal_mean_scale" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/1/worker.log b/benchmarks/v1/evidence/current-worker-044/A11/1/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A11/2/execution.json b/benchmarks/v1/evidence/current-worker-044/A11/2/execution.json new file mode 100644 index 0000000..70d2319 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/2/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "bce1bc8e32b5af811a8c43970760cf8de68a66f22cfbb1098c44374edd952c83", + "predictions.npz": "b609132b89f0ae68a1fcfadc53b8e21fc96556c7adc25d80b2e80ce538f08266", + "training.json": "39ba07e21df03ec6773badf21d24f9615c3d01f0486598d8b829f1fc96faa60b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/2/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 2.845407916000113 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/2/model.bin b/benchmarks/v1/evidence/current-worker-044/A11/2/model.bin new file mode 100644 index 0000000..1c582c1 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/2/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A11", "output": "normal_mean_scale", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.064795107074148, 0.14128297789190805], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 10, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.424527745573136e-15], [-0.3431112553694425], [1.2250717737840986], [-0.7554437811465498], [-0.01905607802782527], [0.7255912667466354], [1.8898482226887807]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [29, 9, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-3.2724462354019906e-16], [-0.10152054704013372], [1.5209742860425568], [0.0800818637056023], [-0.18613018944834878], [0.9355613464284581], [2.102467188836962]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [22, 9, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.4864268312336291e-05], [-0.3666252847257867], [0.9368204301353207], [-0.7113045938985374], [-0.10115618140347053], [0.5541498590104597], [1.701026037960593]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [28, 2, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.06911384972509986], [-0.13962381709738003], [0.612016087999677], [0.21546582871896783], [-0.17853789315444166], [0.27786690642429934], [1.0585580172576252]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 12, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[2.5026732248440262e-05], [-0.2795194422227681], [0.9981328704894047], [-0.5712118476094475], [0.036666474175486026], [0.6883671771494075], [1.7706596439548734]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [27, 5, 14, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.10652924409576107], [-0.15877854974425293], [0.25908228240959696], [0.060309813270310875], [-0.21335557398212113], [0.053745978507485276], [0.583766293117256]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 5, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.80628676447158e-05], [-0.29927680963656295], [0.7648201974742629], [0.2517628445868174], [-0.44763931748062424], [0.5274617713569874], [1.593822151771531]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4140906743705273, 1.6641937047243118, 1.8561937622725964, 2.01618754863739, 2.1569749787449837, 2.2901624888181686, 2.4166998863220215, 2.5522499680519104, 2.661799907684326, 2.7857000827789307, 2.926378071308136, 3.0430875420570374, 3.1528000831604004, 3.2588999271392822, 3.3986124768853188, 3.5416998863220215, 3.653521902859211, 3.7813000679016113, 3.9385156482458115, 4.061625003814697, 4.188540756702423, 4.362225145101547, 4.554784253239632, 4.736900091171265, 4.988993540406227, 5.211318671703339, 5.511846750974655, 5.869337677955627, 6.29079370200634, 6.923943728208542, 8.059512257575989], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 43.0, 44.0, 47.0, 51.0], [3.1712246537208557, 3.536548748612404, 3.743496395647526, 3.916053533554077, 4.0669566839933395, 4.20654770731926, 4.330885082483292, 4.440972566604614, 4.550248757004738, 4.655460149049759, 4.761018678545952, 4.859994292259216, 4.95648954808712, 5.048440366983414, 5.135008245706558, 5.2250635623931885, 5.318016678094864, 5.405156791210175, 5.493605419993401, 5.58842921257019, 5.69901379942894, 5.809252768754959, 5.936196833848953, 6.051661372184753, 6.181209251284599, 6.336206525564194, 6.514555633068085, 6.733340501785278, 7.0229115933179855, 7.404062032699585, 8.116288930177689], [0.920085247606039, 0.9460571631789207, 0.9626455921679735, 0.9736841917037964, 0.9836517386138439, 0.9916735514998436, 1.0, 1.0054201483726501, 1.0111111402511597, 1.01694917678833, 1.022332075983286, 1.027658373117447, 1.0328442677855492, 1.0377358198165894, 1.0428366623818874, 1.0481014251708984, 1.0534703396260738, 1.0589188933372498, 1.0641736425459385, 1.0702199190855026, 1.0767072066664696, 1.0833333730697632, 1.0909091234207153, 1.0986555814743042, 1.1084185428917408, 1.1196020543575287, 1.1341463327407837, 1.1519456058740616, 1.1775362268090248, 1.2303407415747643, 1.408309280872345], [243.9375, 388.0, 486.90625, 565.0, 629.0, 684.0, 733.0, 781.0, 827.0, 872.0, 915.0, 962.0, 1005.0, 1056.0, 1109.0, 1158.0, 1213.0, 1273.0, 1331.0, 1392.0, 1469.0, 1537.3125, 1625.28125, 1715.0, 1818.21875, 1947.0, 2095.15625, 2302.25, 2626.0, 3036.0, 3898.15625], [1.7687265165150166, 1.9387174099683762, 2.0594312474131584, 2.1588569581508636, 2.2362126782536507, 2.3078462928533554, 2.3739964738488197, 2.429102659225464, 2.482028290629387, 2.5324473679065704, 2.5834551379084587, 2.6309240460395813, 2.676671139895916, 2.722113326191902, 2.768068239092827, 2.817671775817871, 2.865021772682667, 2.9171401262283325, 2.9667041823267937, 3.0213107764720917, 3.076394133269787, 3.1423522531986237, 3.209364391863346, 3.2829545736312866, 3.365934543311596, 3.471882089972496, 3.5939647182822227, 3.737350344657898, 3.9240731969475746, 4.188769638538361, 4.600036203861237], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.72999954223633, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.2599983215332, 34.540000915527344, 35.40999984741211, 36.61000061035156, 36.93000030517578, 37.29999923706055, 37.400001525878906, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.97999954223633, 38.25, 38.540000915527344, 38.70000076293945, 39.420310735702515], [-122.62999725341797, -122.43062543869019, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.5337495803833, -119.81999969482422, -119.44999694824219, -119.0, -118.5, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.1500015258789, -118.08999633789062, -118.01000213623047, -117.93000030517578, -117.8499984741211, -117.66999816894531, -117.36000061035156, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [5, 0, 0, -1, -1, -1, -1], "threshold": [6, 16, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.1291575554363796], [0.06242034166733254], [-0.18280027387329834], [-0.11917734382387841], [0.3573462815633488], [-0.20387491200114263], [0.39472354382591585]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/2/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A11/2/predictions.npz new file mode 100644 index 0000000..a197778 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/2/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/2/replay.npz b/benchmarks/v1/evidence/current-worker-044/A11/2/replay.npz new file mode 100644 index 0000000..a197778 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/2/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/2/training.json b/benchmarks/v1/evidence/current-worker-044/A11/2/training.json new file mode 100644 index 0000000..e4bd8c5 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/2/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3975903664290232, + "last_score": 1.3975903664290232, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "5531f652eb58399cca7ec21c838e3d4d17d7e1d57cb72261d6f8b1a9c819f810", + "best_validation_score": 1.3975903664290232, + "output": "normal_mean_scale" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/2/worker.log b/benchmarks/v1/evidence/current-worker-044/A11/2/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A11/3/execution.json b/benchmarks/v1/evidence/current-worker-044/A11/3/execution.json new file mode 100644 index 0000000..4fc4391 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/3/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "0ab4bded3b160b47384b51901791da8aa2dfd7dddf0d66850951d643dc987ff8", + "predictions.npz": "6829b59148582b251c9624bde9d160c960616de33cbe236fe8eb7c566de76e2c", + "training.json": "8b8c4b4000178947f0b7153b2a1f3db4a6f9ab9007c764512e9ba5f1f9e3787b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/3/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 2.8043888749962207 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/3/model.bin b/benchmarks/v1/evidence/current-worker-044/A11/3/model.bin new file mode 100644 index 0000000..b37449f --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/3/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A11", "output": "normal_mean_scale", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0683685283474986, 0.14673311277983175], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 11, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[2.318945286994207e-15], [-0.34067098292810866], [1.2198137783691059], [-0.7208156642258849], [0.010032384213516349], [0.8329913160322199], [2.1815525377650022]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [29, 2, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[2.221656223722048e-16], [-0.10380172824317209], [1.5551507956535193], [0.3329450058773195], [-0.1513368194548914], [0.9601703356278456], [2.146123135071848]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [25, 11, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.8601215777203224e-05], [-0.2798742051769945], [1.2124857954170116], [-0.6487496125614379], [0.036492538439308025], [0.8358640751510085], [1.9636787746384978]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [28, 5, 14, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.06998643494824879], [-0.13924867452664938], [0.5990921910955715], [0.12199138952149546], [-0.20338371814227543], [0.2874590061111157], [1.024384264656584]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 4, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.4401859444601856e-05], [-0.3294013862169381], [0.8417787620929684], [0.2954111731125467], [-0.46718504491928853], [0.48275454058040346], [1.5587938118448963]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [27, 2, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.10609272484191311], [-0.15818218801061776], [0.2584002416423332], [0.20259527332714689], [-0.19916674309195373], [0.0391344769241954], [0.5370868743896952]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [19, 3, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.605876256764401e-05], [-0.36874152170345414], [0.6146390866051479], [0.24352781753575087], [-0.4761533975975823], [0.35150864221367967], [1.4030485781485351]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.438371904194355, 1.6863437071442604, 1.8714531436562538, 2.0336749851703644, 2.1770999431610107, 2.2992812246084213, 2.4375, 2.5666249990463257, 2.672259382903576, 2.8036000728607178, 2.946899890899658, 3.0541999340057373, 3.1635000705718994, 3.2708001136779785, 3.40401254594326, 3.547100067138672, 3.6619468703866005, 3.7885000705718994, 3.9583001136779785, 4.08047491312027, 4.220634385943413, 4.389699935913086, 4.579978302121162, 4.767899990081787, 5.0, 5.201537489891052, 5.494056135416031, 5.827400028705597, 6.256009429693222, 6.864312291145325, 8.10397806763649], [6.0, 10.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 41.0, 42.0, 44.0, 46.0, 51.0], [3.1908708661794662, 3.5544982701539993, 3.769538328051567, 3.9391797184944153, 4.096631303429604, 4.231324911117554, 4.3485307693481445, 4.461934208869934, 4.560636833310127, 4.6689508855342865, 4.767532914876938, 4.8667290806770325, 4.960380792617798, 5.0520704090595245, 5.144468158483505, 5.236564636230469, 5.330353036522865, 5.420070499181747, 5.507213070988655, 5.606712222099304, 5.71323649585247, 5.815960317850113, 5.941916346549988, 6.058565378189087, 6.18639212846756, 6.335129082202911, 6.5092103481292725, 6.74438351392746, 7.044500842690468, 7.432779639959335, 8.172782331705093], [0.9189189076423645, 0.9472385048866272, 0.9635229930281639, 0.9752359762787819, 0.9841269850730896, 0.9923118986189365, 1.0, 1.0056127309799194, 1.011356383562088, 1.0172910690307617, 1.0230078808963299, 1.0281088799238205, 1.0334572792053223, 1.038102276623249, 1.0431058630347252, 1.0483048558235168, 1.053540661931038, 1.05896857380867, 1.064365066587925, 1.07060307264328, 1.0769398100674152, 1.0843018889427185, 1.091428518295288, 1.0994166135787964, 1.1084861122071743, 1.12039315700531, 1.1343283653259277, 1.1525585353374481, 1.179761990904808, 1.2346722707152367, 1.4198649637401104], [249.0, 392.0, 488.0, 562.875, 629.0, 685.0, 733.0, 782.0, 828.0, 872.6875, 920.0, 967.0, 1014.0, 1060.0, 1110.53125, 1163.0, 1217.0, 1275.0, 1335.0, 1401.0, 1476.0, 1545.0, 1629.0, 1723.0, 1830.0, 1959.1875, 2126.0, 2332.0, 2645.09375, 3073.4375, 3916.0625], [1.7458723820745945, 1.9243162646889687, 2.0589951425790787, 2.16470006108284, 2.2429539784789085, 2.3117504119873047, 2.3733991533517838, 2.4285714626312256, 2.479019284248352, 2.529907315969467, 2.581305742263794, 2.6310383677482605, 2.677419424057007, 2.7241829484701157, 2.77199849486351, 2.8215478658676147, 2.8680851608514786, 2.919417142868042, 2.970695249736309, 3.02358141541481, 3.076923131942749, 3.1407352685928345, 3.2090568393468857, 3.282976984977722, 3.368819795548916, 3.472125470638275, 3.5799401625990868, 3.722441017627716, 3.913372151553631, 4.192546337842941, 4.602068901062012], [32.75, 32.91999816894531, 33.519062757492065, 33.720001220703125, 33.779998779296875, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.470001220703125, 35.369998931884766, 36.5099983215332, 36.86000061035156, 37.290000915527344, 37.38999938964844, 37.59000015258789, 37.709999084472656, 37.77000045776367, 37.86000061035156, 37.970001220703125, 38.15999984741211, 38.529998779296875, 38.70000076293945, 39.47031366825104], [-122.56999969482422, -122.43000030517578, -122.30000305175781, -122.2300033569336, -122.11000061035156, -122.0199966430664, -121.91999816894531, -121.80000305175781, -121.4800033569336, -121.30999755859375, -121.02999877929688, -120.45999908447266, -119.79000091552734, -119.31999969482422, -118.94999694824219, -118.4800033569336, -118.4000015258789, -118.3499984741211, -118.30000305175781, -118.25, -118.19000244140625, -118.13999938964844, -118.08000183105469, -117.98999786376953, -117.91999816894531, -117.82812643051147, -117.63999938964844, -117.30999755859375, -117.20999908447266, -117.0999984741211, -116.98999786376953], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [5, 0, 0, -1, -1, -1, -1], "threshold": [9, 21, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.12961961316104983], [0.034041233837031525], [-0.20400616854759224], [-0.07408096841907844], [0.35748101704671686], [-0.22665515099609515], [0.39418967440582003]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/3/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A11/3/predictions.npz new file mode 100644 index 0000000..b2f8cf0 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/3/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/3/replay.npz b/benchmarks/v1/evidence/current-worker-044/A11/3/replay.npz new file mode 100644 index 0000000..b2f8cf0 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/3/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/3/training.json b/benchmarks/v1/evidence/current-worker-044/A11/3/training.json new file mode 100644 index 0000000..d29952d --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/3/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3802549264330797, + "last_score": 1.3802549264330797, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "51b979befa9d5496a62eaf84da980f0fcd0c0fcf7f85f2fce2471afc1c14bcc4", + "best_validation_score": 1.3802549264330797, + "output": "normal_mean_scale" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/3/worker.log b/benchmarks/v1/evidence/current-worker-044/A11/3/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/A11/4/execution.json b/benchmarks/v1/evidence/current-worker-044/A11/4/execution.json new file mode 100644 index 0000000..f159fbb --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/4/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "6ada131dadd78867617f34d4bded6e53c49e38ec18496911c2d3e576eb550b25", + "predictions.npz": "99bbee506d5f6151fbfa2ab490acda1d41839bd0e0a7cc1ef33b0c25a728a85a", + "training.json": "9c1be928356410365d02b711f01d7bb3e4abc8c050bc6d426dbc78f628898190", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/4/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 2.8942160829901695 +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/4/model.bin b/benchmarks/v1/evidence/current-worker-044/A11/4/model.bin new file mode 100644 index 0000000..172d573 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/4/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A11", "output": "normal_mean_scale", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "base": [2.0699676008743415, 0.1478512966389003], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [24, 10, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.307073694692759e-15], [-0.34436226287684346], [1.229538470699598], [-0.7564201238944974], [-0.020522673565856044], [0.8509973210827574], [2.173572183199696]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [29, 2, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.4687186062067365e-16], [-0.10267574624727047], [1.5382814060348915], [0.30596920501269315], [-0.1469354908243255], [0.9869780030880609], [2.0856201661826748]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [26, 12, 29, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[1.814797502605515e-05], [-0.2533693636374005], [1.3677348206001232], [-0.6299437882703293], [0.09662676391760686], [0.9738818052338085], [1.9564954258066176]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [28, 2, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.06903196003503421], [-0.14037002996525996], [0.6200967222858763], [0.22055413548891584], [-0.18012170144048378], [0.2824633864197917], [1.0744784831672045]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [22, 5, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[3.568772097960926e-05], [-0.33374772841059674], [0.8528887114625858], [0.24138525698033864], [-0.49120527402653547], [0.4920115860841442], [1.5735984133065675]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 1, -1, -1, -1, -1], "threshold": [27, 5, 13, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.10597043116324971], [-0.1587715519434363], [0.26350130306918135], [0.06748589365110308], [-0.21562453514484195], [0.04472770918743803], [0.5700541730845449]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 5, 0, -1, -1, -1, -1], "threshold": [19, 4, 28, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[4.6780151803649935e-05], [-0.3759414444221267], [0.626639847890591], [0.16547069099795733], [-0.4996994107284261], [0.3630641541258528], [1.416373993526111]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"], "cuts": [[1.4065906219184399, 1.6796687692403793, 1.8703624568879604, 2.02839994430542, 2.1683874651789665, 2.2890436947345734, 2.419100046157837, 2.5555999279022217, 2.6607000827789307, 2.7801374942064285, 2.9247937500476837, 3.0467875599861145, 3.1512999534606934, 3.25, 3.3905999660491943, 3.5283000469207764, 3.6484938338398933, 3.7778000831604004, 3.9336812123656273, 4.062724947929382, 4.203100204467773, 4.375, 4.566340461373329, 4.772974848747253, 5.025865837931633, 5.24970006942749, 5.528915718197823, 5.868662416934967, 6.276168778538704, 6.894131183624268, 8.105043292045593], [6.0, 9.0, 12.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 21.0, 22.0, 24.0, 25.0, 26.0, 27.0, 29.0, 30.0, 31.0, 33.0, 34.0, 35.0, 36.0, 37.0, 39.0, 40.0, 42.0, 44.0, 46.0, 50.0], [3.1845124438405037, 3.54721999168396, 3.742460750043392, 3.9211689829826355, 4.064633622765541, 4.200572043657303, 4.316634356975555, 4.4322593212127686, 4.534805074334145, 4.638281583786011, 4.749698907136917, 4.845510125160217, 4.940820962190628, 5.038151234388351, 5.1308262795209885, 5.232561349868774, 5.327221110463142, 5.418576866388321, 5.503131210803986, 5.6082152128219604, 5.712205603718758, 5.818505018949509, 5.943592935800552, 6.071043014526367, 6.20113331079483, 6.351996213197708, 6.529425621032715, 6.751164793968201, 7.049741446971893, 7.466062933206558, 8.234625190496445], [0.9211222250014544, 0.9471822455525398, 0.9629629850387573, 0.9752832278609276, 0.984432639554143, 0.9928057789802551, 1.0, 1.006282776594162, 1.01141944155097, 1.0172979533672333, 1.0232220701873302, 1.0287317484617233, 1.0338983535766602, 1.0387213602662086, 1.0442398115992546, 1.0493013858795166, 1.0544538758695126, 1.0596816539764404, 1.0646697767078876, 1.0710852593183517, 1.077187903225422, 1.0843179300427437, 1.091883659362793, 1.100000023841858, 1.1089529432356358, 1.1207945346832275, 1.134757850319147, 1.1528950929641724, 1.1797465905547142, 1.2394086867570877, 1.4396607726812363], [236.96875, 381.0, 484.0, 557.0, 627.0, 685.8125, 736.0, 783.0, 831.0, 877.0, 924.0, 971.0, 1019.0, 1065.0, 1115.53125, 1167.0, 1220.0, 1276.0, 1337.0, 1404.0, 1478.34375, 1548.3125, 1633.28125, 1726.0, 1836.21875, 1963.0, 2106.0, 2325.0, 2623.09375, 3036.0, 3870.0625], [1.7455518245697021, 1.9239856451749802, 2.0518675968050957, 2.1600000858306885, 2.2446145340800285, 2.310344934463501, 2.375324472784996, 2.4291751980781555, 2.480269528925419, 2.5325416922569275, 2.584825038909912, 2.633333444595337, 2.677319936454296, 2.7208177596330643, 2.7680302932858467, 2.81711745262146, 2.8630683943629265, 2.914718523621559, 2.967287130653858, 3.0196740925312042, 3.0738572478294373, 3.137055739760399, 3.205169565975666, 3.2814892530441284, 3.3642694130539894, 3.4685258716344833, 3.582284726202488, 3.722460836172104, 3.919498160481453, 4.203756481409073, 4.6049089431762695], [32.7599983215332, 32.9900016784668, 33.599998474121094, 33.720001220703125, 33.790000915527344, 33.84000015258789, 33.88999938964844, 33.93000030517578, 33.970001220703125, 34.0099983215332, 34.040000915527344, 34.06999969482422, 34.099998474121094, 34.13999938964844, 34.18000030517578, 34.25, 34.4900016784668, 35.380001068115234, 36.56406319141388, 36.849998474121094, 37.290000915527344, 37.38999938964844, 37.599998474121094, 37.709999084472656, 37.779998779296875, 37.86000061035156, 37.970001220703125, 38.25, 38.540000915527344, 38.709999084472656, 39.45000076293945], [-122.58999633789062, -122.43000030517578, -122.30000305175781, -122.22000122070312, -122.0999984741211, -122.01000213623047, -121.91000366210938, -121.79250144958496, -121.47000122070312, -121.30000305175781, -121.01000213623047, -120.4800033569336, -119.80000305175781, -119.33999633789062, -118.97000122070312, -118.48999786376953, -118.41000366210938, -118.3499984741211, -118.30000305175781, -118.26000213623047, -118.19999694824219, -118.13999938964844, -118.08999633789062, -118.0, -117.93000030517578, -117.8499984741211, -117.68000030517578, -117.36875247955322, -117.2300033569336, -117.11000061035156, -117.01000213623047], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [5, 0, 0, -1, -1, -1, -1], "threshold": [10, 18, 30, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.13019289599524042], [0.022373503053450663], [-0.21010154932365263], [-0.10986770141312176], [0.2693867876913076], [-0.23263793857809612], [0.3746746766493578]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null}} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/4/predictions.npz b/benchmarks/v1/evidence/current-worker-044/A11/4/predictions.npz new file mode 100644 index 0000000..9f8ca53 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/4/predictions.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/4/replay.npz b/benchmarks/v1/evidence/current-worker-044/A11/4/replay.npz new file mode 100644 index 0000000..9f8ca53 Binary files /dev/null and b/benchmarks/v1/evidence/current-worker-044/A11/4/replay.npz differ diff --git a/benchmarks/v1/evidence/current-worker-044/A11/4/training.json b/benchmarks/v1/evidence/current-worker-044/A11/4/training.json new file mode 100644 index 0000000..4313204 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/A11/4/training.json @@ -0,0 +1,17 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3968030699108356, + "last_score": 1.3968030699108356, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "c0e249e62fb1b511f42e396a12c2969f01fde250fc69070899bd1193f1671ae1", + "best_validation_score": 1.3968030699108356, + "output": "normal_mean_scale" +} diff --git a/benchmarks/v1/evidence/current-worker-044/A11/4/worker.log b/benchmarks/v1/evidence/current-worker-044/A11/4/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/current-worker-044/README.md b/benchmarks/v1/evidence/current-worker-044/README.md new file mode 100644 index 0000000..30c6d17 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/README.md @@ -0,0 +1,24 @@ +# Current OpenBoost A1/A11 validation integration + +All ten cells pass: two tasks on each of five frozen housing folds. Four rounds +per trial, depth two, 32 bins and patience three. This is plumbing, not a quality +search or a performance result. No test scores were computed. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-current-worker-044 +``` + +Requires the pinned local housing archive and committed preprocessing freeze. +`summary.json` records revision/dirty state, source hashes, full jobs, runtime +identity, source and packet provenance, bounded worker outcomes and replay checks. +Each application/fold directory preserves raw validation predictions, JSON model, +training metadata, process evidence/log and fresh-process predictions. SHA256s +link these to the run. Generated training/validation/test packets are not copied; +their manifest hashes and deterministic export path permit reconstruction. + +Validation predictions replay exactly; A1 outputs means, A11 outputs means and +positive standard deviations in original target units. Training selected the +strict best validation snapshot, independently of training-step acceptance. +The runtime supports CPU only. Memory was not capped; wall times are execution +records, not fair speed measurements. Packet separation is not OS label isolation. +This is partial M3 evidence, not F0.3/E3 or full delivery acceptance. diff --git a/benchmarks/v1/evidence/current-worker-044/summary.json b/benchmarks/v1/evidence/current-worker-044/summary.json new file mode 100644 index 0000000..df7fc64 --- /dev/null +++ b/benchmarks/v1/evidence/current-worker-044/summary.json @@ -0,0 +1,998 @@ +{ + "scope": "Current A1/A11 real-data validation plumbing only; four rounds, no test scores, quality or performance claim", + "revision": "03ff34e88c4f345e08256af5b0ec354451792894", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "benchmarks.v1.openboost_worker_smoke", + "/private/tmp/openboost-current-worker-044" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "gpu": null, + "threads": 1, + "memory_cap": null, + "packages": { + "numpy": "2.3.5", + "openboost": "1.0.0rc1" + }, + "sources": { + "src/openboost/__init__.py": "b0a76b68a1824abef45bf30d3d094dce7de2ec9deef3d5cbe862a6b3f6038788", + "src/openboost/artifacts.py": "fc5232f29d2b9d55676035bbd5bb9fe726babba5fcdfbd84ff18133de3e9a817", + "src/openboost/binning.py": "e77bdbaf42121ba5dde8ec681371bd0c7d1e31fada19618d796279cc0b322a7d", + "src/openboost/composition.py": "a52dc4d09582031441dbf2c4a3ed889851783a2f4fece50506fa5e0e0501ccec", + "src/openboost/data.py": "93b4eb6487e7fb21c960d19906c7a49b3df9fa28b2a2bacdc8ea6437889f2343", + "src/openboost/leaves.py": "3bff04ff4b1eff1a1d58f6ce5e29f15391e8b2e9a80a696c6fee988d1be95b75", + "src/openboost/multioutput.py": "98c192719bb9ee787ec385a2d219a2f52aacebc161cc9fc1275006f49daad76d", + "src/openboost/objectives.py": "fcf847bda2917354093e03ce42bd22e0fd85e391185d3141318cb7ccecd721f7", + "src/openboost/ops.py": "aa73cc40df7c79203505f91e3b86292dffbb3b9348467be60d8587e3bfd8c6ad", + "src/openboost/outputs.py": "f10f3a68b4513cb93c222cd1e39ca238c686c903daa8c8035c417b5916365115", + "src/openboost/ranking.py": "beddf8fb6eeecbed54acdb55147bc251392a6f9fb7b46304e5d80d1d0ee642b3", + "src/openboost/recipes.py": "4fc1cd9971cde4bad95922c0f4e241d169a8a9fa08730070f02033c3405d265c", + "src/openboost/results.py": "b33bb8c658678bf5319a1e76b9a420112debea4f26361acad166f6f1e641585d", + "src/openboost/runs.py": "5a00eead8d6d4d6ddc99a646a0131f04918c619c5061b647ec4fc3c386459dc4", + "src/openboost/runtime.py": "8962e0204d867a1b700ed15e92b155cd6144927b651a79b8083634a0c315ad2d", + "src/openboost/stats.py": "4edbeba39e727f60213f04b5d3592bc82fb02cb1ebd9603ca9f8cee66b932cc9", + "src/openboost/stopping.py": "2374873a01dba8d7b359cd484337de228ca6c805d6a43b23bcf51598416746a1", + "src/openboost/survival.py": "cb168551a5aa026d6c501f4370c7ddc998d3cc7269ce6f17d93258ceadef89e9", + "src/openboost/tree.py": "60dd8cbc56d78913e868a3788bd08c91e858febc4f4c0534831077df640d7581", + "benchmarks/v1/openboost_worker.py": "6d3ea3782532cd020638534fc41772ae4874808ebf9b59c6986fa3786338e3ee", + "benchmarks/v1/openboost_predict.py": "de8fea83080109559a57a6c6710e3545384665edd7a07848ed284fd53ee2a535", + "benchmarks/v1/openboost_worker_smoke.py": "149007c143541ea8682417dc349be36ff6e9f2e54d417762c78621373a97137d", + "benchmarks/v1/worker_data.py": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "benchmarks/v1/process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A1": { + "application": "A1", + "dataset": "housing", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "ee9fb6d9cbbc8ee3088413ffefb7467c221df5606372269f81b5225087007928" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "610e546bd63e5b7ad2c1be26db958fd00536b6d9e4026f102e29ebfcf003cece" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e4f404f717fa4b6c6fbea5819f4fa572336d4b96fd351c824a27373d78eba473" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "cacb45b11d4ceeb3997895e9bee1398a2bf8a8711fabffdc1dedd96d19e66f10" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "07180ad525f246a21d67918781b061f53b76249f9775e8aa010ad39eaa1b2631" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "9b73a363988ddc95d2b7030ca1b387d22e9e7f44bc30f694058f251f718f4a7e" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "0c5fe3cf4b800fee9346ffc9214815d6f93064b3a0482fc15b78e231d3888102" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "492984cc05178c60813ddcb62e798c4c9aaa075273537b2d5bc7ed5f2cbf438d" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4130a6ee488ec32a4555f3f9f55bbbc5dac85ee4f55b1d2a6d6d0c0bcb35e82f" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "f0d1d8c9180e58156138c18fbfa7e9b10fbba3aac5fcf26054f0c6e84d0fbd37" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bdc4220a156117b1656b4867ef417c1988eb89fa8cf6d8f19a41b17214845305" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "4d267b0afe6968592ea8010a34aa7571922c900d6a2baa264d3ecac8f44021db" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4ab22696f06c57eb49ec3f94788aa5a59e8417c3eaaf1f994b3210a3b34ed327" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "66749ebd46b211b0d8d8f4b9efb458a5ffcd5a3620ed6b0766f5d0e312b8e20a" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "1442212a3dd1bad69b848fef0a9d4746688e614edfe4f99cb84add5c3630a1b1" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3fc154610f4ff93c172802d57afa274c299940c638dacd4869082c8bc5bc3c51" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "a80fd6416a556871e3f57ea5924f6b5f26adca2a55e9f6991495777600b4a2f5" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "98fdf48b758b632e87581c66517e9c9b89e09e46f4fbeca3eb7ccbba3e443e67" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "d52e364a351d6424c29c95d226d3962c9727e9b61acfbd5ec9861bddd3444ed3" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "d02417bd952ef72b6da1865cfac5f7667ef329897eaac3bc817b3dd291ab4369" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "a3cd76073e4a14f666ed41182287b86009dbde10ec7cc0637926aa30d3b46783" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "b798bb52c90d3c9866582b355d3df6cfd690e97fa32cfe5b69cd19d8f9bb6a41" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "677e837444eb3338e6a23a93f2ab693bb5afd1b96c3ea08023af41c4befb29ce" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bbf8716a861ff6d59ce784d7b0e948c43484f8f2226c9f0f601a1ed01195d613" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "f769ecfe75a7cf24ce3ad9ae86132365333baf65c7372de2c8f03960636a790d" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "93a5e050bdf4c3ae0c526fb0a75bd806df404c8ab558e3019bcc0bdcd28448ae", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A11": { + "application": "A11", + "dataset": "housing", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "ee9fb6d9cbbc8ee3088413ffefb7467c221df5606372269f81b5225087007928" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "610e546bd63e5b7ad2c1be26db958fd00536b6d9e4026f102e29ebfcf003cece" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e4f404f717fa4b6c6fbea5819f4fa572336d4b96fd351c824a27373d78eba473" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "cacb45b11d4ceeb3997895e9bee1398a2bf8a8711fabffdc1dedd96d19e66f10" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "07180ad525f246a21d67918781b061f53b76249f9775e8aa010ad39eaa1b2631" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "9b73a363988ddc95d2b7030ca1b387d22e9e7f44bc30f694058f251f718f4a7e" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "0c5fe3cf4b800fee9346ffc9214815d6f93064b3a0482fc15b78e231d3888102" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "492984cc05178c60813ddcb62e798c4c9aaa075273537b2d5bc7ed5f2cbf438d" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4130a6ee488ec32a4555f3f9f55bbbc5dac85ee4f55b1d2a6d6d0c0bcb35e82f" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "f0d1d8c9180e58156138c18fbfa7e9b10fbba3aac5fcf26054f0c6e84d0fbd37" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bdc4220a156117b1656b4867ef417c1988eb89fa8cf6d8f19a41b17214845305" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "4d267b0afe6968592ea8010a34aa7571922c900d6a2baa264d3ecac8f44021db" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4ab22696f06c57eb49ec3f94788aa5a59e8417c3eaaf1f994b3210a3b34ed327" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "66749ebd46b211b0d8d8f4b9efb458a5ffcd5a3620ed6b0766f5d0e312b8e20a" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "1442212a3dd1bad69b848fef0a9d4746688e614edfe4f99cb84add5c3630a1b1" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3fc154610f4ff93c172802d57afa274c299940c638dacd4869082c8bc5bc3c51" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "a80fd6416a556871e3f57ea5924f6b5f26adca2a55e9f6991495777600b4a2f5" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "98fdf48b758b632e87581c66517e9c9b89e09e46f4fbeca3eb7ccbba3e443e67" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "d52e364a351d6424c29c95d226d3962c9727e9b61acfbd5ec9861bddd3444ed3" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "d02417bd952ef72b6da1865cfac5f7667ef329897eaac3bc817b3dd291ab4369" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "a3cd76073e4a14f666ed41182287b86009dbde10ec7cc0637926aa30d3b46783" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "b798bb52c90d3c9866582b355d3df6cfd690e97fa32cfe5b69cd19d8f9bb6a41" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "677e837444eb3338e6a23a93f2ab693bb5afd1b96c3ea08023af41c4befb29ce" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bbf8716a861ff6d59ce784d7b0e948c43484f8f2226c9f0f601a1ed01195d613" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "f769ecfe75a7cf24ce3ad9ae86132365333baf65c7372de2c8f03960636a790d" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "93a5e050bdf4c3ae0c526fb0a75bd806df404c8ab558e3019bcc0bdcd28448ae", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/0/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.530277166995802, + "artifacts": { + "model.bin": "42482c71af73600b32504cd2300db1cb3fa6b57f72fae08e0478a16dc496ff3c", + "training.json": "1e6e4718642f9d2f10e36bdcf630cde271c5b2a2bc2e678c80306a8817279291", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "772ef83d58e58e04a32a3ede96621ef5aa1b4e084d780d459e50f5159aa594a3" + }, + "application": "A1", + "fold": 0, + "job": { + "application": "A1", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 0, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A1/0/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A1/0/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A1/0/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A1/0/fit/replay.npz" + ], + "prediction_shape": [ + 4128 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4909141515050642, + "last_score": 0.4909141515050642, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "ab5a940ba2c8d3c29d6573cfbfc967b5ddd8bd78b53e00faa0c95af4dd43fccd", + "best_validation_score": 0.4909141515050642, + "output": "mean" + }, + "replay_sha256": "772ef83d58e58e04a32a3ede96621ef5aa1b4e084d780d459e50f5159aa594a3" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/1/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.4940680830040947, + "artifacts": { + "model.bin": "66abe5785919193ef683879004ac629a2a96db4d0e97e98560bdd1b1fffaf108", + "training.json": "f55ee5edc1ad857690013c7b09ad8589daeefc95326b7e2f0abc730f7b36bb86", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "ca52ffc4433012145f3e58d40fa62064a199f1d95d0cc2c0fe631ed139656680" + }, + "application": "A1", + "fold": 1, + "job": { + "application": "A1", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 1, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A1/1/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A1/1/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A1/1/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A1/1/fit/replay.npz" + ], + "prediction_shape": [ + 4128 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.5102077406604998, + "last_score": 0.5102077406604998, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "87b5e02b84cb16887c94ab0341c7194069f6a861f29eb707da2e204df515c797", + "best_validation_score": 0.5102077406604998, + "output": "mean" + }, + "replay_sha256": "ca52ffc4433012145f3e58d40fa62064a199f1d95d0cc2c0fe631ed139656680" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/2/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.5269187920057448, + "artifacts": { + "model.bin": "e99803d4a6a65fc6f8772c8e2b33f2c22b742c303f4a37efc6138e0ec5e6b528", + "training.json": "83e73d9178b13588ec36e1e9b7d3290b9667393ff2e235ac739a5b3b8a838aca", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6917f874b654058be1e40a8ae8b3e7c134d7b5d2658057e27a857a7f3c4d3a5d" + }, + "application": "A1", + "fold": 2, + "job": { + "application": "A1", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 2, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A1/2/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A1/2/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A1/2/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A1/2/fit/replay.npz" + ], + "prediction_shape": [ + 4128 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.5128165758282013, + "last_score": 0.5128165758282013, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "6cf9181f4d2549e39573ee13a09a3c3adeafdaca25dddcb0b26cd2112eea6022", + "best_validation_score": 0.5128165758282013, + "output": "mean" + }, + "replay_sha256": "6917f874b654058be1e40a8ae8b3e7c134d7b5d2658057e27a857a7f3c4d3a5d" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/3/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.4972040409920737, + "artifacts": { + "model.bin": "1266d5f96d6943d23fe6e504d7c1084335c355eef80f045592a14c1ae58122da", + "training.json": "cde5a96cb1e8451f8336aca661b93b07a47c691823aeb85bd9a9e98590261aa8", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "afc87a92eaccbd2d607a4e50a704c8acb4e442a402f4e9f6c8c3d9746e9dfa64" + }, + "application": "A1", + "fold": 3, + "job": { + "application": "A1", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 3, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A1/3/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A1/3/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A1/3/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A1/3/fit/replay.npz" + ], + "prediction_shape": [ + 4128 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.4828317821177374, + "last_score": 0.4828317821177374, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "b45a3dc822269c25aac6a2a3ee132a16b72bef07c5fcd213fcf91b1a0b2d114b", + "best_validation_score": 0.4828317821177374, + "output": "mean" + }, + "replay_sha256": "afc87a92eaccbd2d607a4e50a704c8acb4e442a402f4e9f6c8c3d9746e9dfa64" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A1/4/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.5469587090046844, + "artifacts": { + "model.bin": "8dc0b4ed0f2c150c74470075e8ceb94298361343d5b025b799bdd29da8365981", + "training.json": "f0615247ee36b25908a15bc92faafbbe911f78d6d54a61d438a0385a0c3a703f", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "b66b8e66b9b03c6503ddcf18107792d7c64eb8197fe8d695006b49606979b4c4" + }, + "application": "A1", + "fold": 4, + "job": { + "application": "A1", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 4, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A1/4/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A1/4/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A1/4/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A1/4/fit/replay.npz" + ], + "prediction_shape": [ + 4128 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.504057415396101, + "last_score": 0.504057415396101, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "b2380388e213d2888ee48c373475e2843e7fe21f11ccb0db504f81c99845f731", + "best_validation_score": 0.504057415396101, + "output": "mean" + }, + "replay_sha256": "b66b8e66b9b03c6503ddcf18107792d7c64eb8197fe8d695006b49606979b4c4" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/0/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 3.0241687500092667, + "artifacts": { + "model.bin": "0408cb3749259fced07bfe06f6a226d5abcd1340e70c8df0aa25a54d3bce4933", + "training.json": "60278e98745d17c90171b138595bf91f1caf15650ee5a25d4d6c5de28e57b23b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "4246f7a6c7c45c369c2a1e9963b0ff399c7cce50268bedc633f4cd75f14beb3c" + }, + "application": "A11", + "fold": 0, + "job": { + "application": "A11", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 0, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A11/0/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A11/0/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A11/0/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A11/0/fit/replay.npz" + ], + "prediction_shape": [ + 4128, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3881190556099985, + "last_score": 1.3881190556099985, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "8428770340ff7481c5556ef396f5a01a4f5cede7fa42560dcd74b96519f9d697", + "best_validation_score": 1.3881190556099985, + "output": "normal_mean_scale" + }, + "replay_sha256": "4246f7a6c7c45c369c2a1e9963b0ff399c7cce50268bedc633f4cd75f14beb3c" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/1/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.8695160830102395, + "artifacts": { + "model.bin": "c1c64c505259fb2e08cc4cd9216b8abf5e9522057c50ecb7225692d1c83237e1", + "training.json": "10c1263a6c6edd20136887586438b06ac1011693d7f9c674d898eb542fd85dce", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "fed0a1dbe065d670a30cf469277d1972b311526e932ae6bb6c7e7e6511185964" + }, + "application": "A11", + "fold": 1, + "job": { + "application": "A11", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 1, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A11/1/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A11/1/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A11/1/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A11/1/fit/replay.npz" + ], + "prediction_shape": [ + 4128, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.394201272104827, + "last_score": 1.394201272104827, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "7b540ad7b9493b90c52e31501a9a3107f41d0c01ebcee1bc4394efc2fd84cb22", + "best_validation_score": 1.394201272104827, + "output": "normal_mean_scale" + }, + "replay_sha256": "fed0a1dbe065d670a30cf469277d1972b311526e932ae6bb6c7e7e6511185964" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/2/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.845407916000113, + "artifacts": { + "model.bin": "bce1bc8e32b5af811a8c43970760cf8de68a66f22cfbb1098c44374edd952c83", + "training.json": "39ba07e21df03ec6773badf21d24f9615c3d01f0486598d8b829f1fc96faa60b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "b609132b89f0ae68a1fcfadc53b8e21fc96556c7adc25d80b2e80ce538f08266" + }, + "application": "A11", + "fold": 2, + "job": { + "application": "A11", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 2, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A11/2/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A11/2/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A11/2/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A11/2/fit/replay.npz" + ], + "prediction_shape": [ + 4128, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3975903664290232, + "last_score": 1.3975903664290232, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "5531f652eb58399cca7ec21c838e3d4d17d7e1d57cb72261d6f8b1a9c819f810", + "best_validation_score": 1.3975903664290232, + "output": "normal_mean_scale" + }, + "replay_sha256": "b609132b89f0ae68a1fcfadc53b8e21fc96556c7adc25d80b2e80ce538f08266" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/3/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.8043888749962207, + "artifacts": { + "model.bin": "0ab4bded3b160b47384b51901791da8aa2dfd7dddf0d66850951d643dc987ff8", + "training.json": "8b8c4b4000178947f0b7153b2a1f3db4a6f9ab9007c764512e9ba5f1f9e3787b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6829b59148582b251c9624bde9d160c960616de33cbe236fe8eb7c566de76e2c" + }, + "application": "A11", + "fold": 3, + "job": { + "application": "A11", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 3, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A11/3/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A11/3/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A11/3/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A11/3/fit/replay.npz" + ], + "prediction_shape": [ + 4128, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3802549264330797, + "last_score": 1.3802549264330797, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "51b979befa9d5496a62eaf84da980f0fcd0c0fcf7f85f2fce2471afc1c14bcc4", + "best_validation_score": 1.3802549264330797, + "output": "normal_mean_scale" + }, + "replay_sha256": "6829b59148582b251c9624bde9d160c960616de33cbe236fe8eb7c566de76e2c" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-current-worker-044/A11/4/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.8942160829901695, + "artifacts": { + "model.bin": "6ada131dadd78867617f34d4bded6e53c49e38ec18496911c2d3e576eb550b25", + "training.json": "9c1be928356410365d02b711f01d7bb3e4abc8c050bc6d426dbc78f628898190", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "99bbee506d5f6151fbfa2ab490acda1d41839bd0e0a7cc1ef33b0c25a728a85a" + }, + "application": "A11", + "fold": 4, + "job": { + "application": "A11", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 4, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-current-worker-044/A11/4/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-current-worker-044/A11/4/fit/model.bin", + "/private/tmp/openboost-current-worker-044/A11/4/fit/features.npz", + "/private/tmp/openboost-current-worker-044/A11/4/fit/replay.npz" + ], + "prediction_shape": [ + 4128, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.3968030699108356, + "last_score": 1.3968030699108356, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "c0e249e62fb1b511f42e396a12c2969f01fde250fc69070899bd1193f1671ae1", + "best_validation_score": 1.3968030699108356, + "output": "normal_mean_scale" + }, + "replay_sha256": "99bbee506d5f6151fbfa2ab490acda1d41839bd0e0a7cc1ef33b0c25a728a85a" + } + ] +} diff --git a/benchmarks/v1/evidence/early-stopping-cpu.json b/benchmarks/v1/evidence/early-stopping-cpu.json new file mode 100644 index 0000000..549f9a0 --- /dev/null +++ b/benchmarks/v1/evidence/early-stopping-cpu.json @@ -0,0 +1,1474 @@ +{ + "scope": "synthetic CPU native stopping and replay only; not real quality or GPU evidence", + "cells": [ + { + "library": "xgboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "rmse": [ + 1.3190258050330348, + 1.4036078889311892, + 1.4715428645880309, + 1.5328491276069665 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "logloss": [ + 0.6347751041253408, + 0.5559325362245242, + 0.4902409628033638, + 0.43469255169232685, + 0.3871888145804405, + 0.34620265463987987, + 0.3105856830875079, + 0.2794518657028675, + 0.252102733651797, + 0.2279777045051257, + 0.2066204644739628, + 0.18765461444854736, + 0.17076645642518998, + 0.1556919942299525, + 0.1422073448697726, + 0.13012115868429344, + 0.11926914242406686, + 0.10950897460182508, + 0.10071733991305033, + 0.09278654331962267, + 0.08562237073977788, + 0.07914204808572928, + 0.07327268011868, + 0.06794991387675206 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "mlogloss": [ + 1.100152987241745, + 1.1028474867343903, + 1.1085578600565593, + 1.1105650146802266 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.2858650093484256, + 0.27033074512663813, + 0.2566173301747767, + 0.2440655096123616, + 0.23339290898293258, + 0.2228521027447035, + 0.21307654707278642, + 0.20405924214153653, + 0.19786285429468586, + 0.190334304013393, + 0.184337596114104, + 0.17870675332637298, + 0.1734560108329687, + 0.16988741957789494, + 0.16622945896556807, + 0.16233782992801732, + 0.15888993436636964, + 0.1556452370688526, + 0.15289295974022632, + 0.1503074509015682, + 0.14764388453267102, + 0.1446768974678384, + 0.14224238008157247, + 0.13988932841457427 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "rmse": [ + 1.038138414311526, + 0.9722194066440749, + 0.9178922825886241, + 0.8693654999651096, + 0.8270715338469173, + 0.7883264533981309, + 0.7502198023011187, + 0.7213986959385202, + 0.6897098577511739, + 0.6578282735554082, + 0.6291513744092626, + 0.6033446101602535, + 0.5790913492344661, + 0.5565168521967778, + 0.5372510544938934, + 0.5175092449117404, + 0.4934518913749974, + 0.4743665822195476, + 0.45186885606762267, + 0.43467184476739584, + 0.41942895083190357, + 0.40485971261551096, + 0.3928077869847803, + 0.38511541186146336 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "poisson-nloglik": [ + 2.20390141805013, + 2.137744075059891, + 2.0731852829456328, + 2.0092012087504068, + 1.9474283715089162, + 1.8912934521834055, + 1.8333994368712108, + 1.777717645963033, + 1.7242320875326793, + 1.6736395716667176, + 1.6256426950295766, + 1.5802442669868468, + 1.5382574121157329, + 1.497567985455195, + 1.459828053911527, + 1.4223221321900685, + 1.38948645790418, + 1.3557770192623138, + 1.3250512798627219, + 1.2974480837583542, + 1.2731199165185292, + 1.2558044453461965, + 1.2382590234279633, + 1.224551365772883 + ] + } + } + } + ], + "exposure_doubling_max_abs_error": 2.288818359375e-05 + }, + { + "library": "xgboost", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "gamma-deviance": [ + 1.0479340935785635, + 0.8765117834399765, + 0.7362795183202252, + 0.6253385437477845, + 0.5339218441857763, + 0.4751267791473462, + 0.4329137079960977, + 0.39694250947989834, + 0.3668045233808546, + 0.340728317486014, + 0.31514107499412297, + 0.2947485263991742, + 0.2759979225789607, + 0.25946758926849234, + 0.2446441778893738, + 0.22804094806667383, + 0.2168541413966892, + 0.20469011664560335, + 0.19624207265745403, + 0.1870708379727148, + 0.17879051575085517, + 0.161901100057518, + 0.14734316934094144, + 0.1421370795503511 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 5.518956836064657, + 5.400989929835002, + 5.338087542851766, + 5.288453420003255, + 5.2157480955123905, + 5.173163398106893, + 5.1388852516810095, + 5.105915009975433, + 5.078025571505228, + 5.053517389297485, + 5.031838723023733, + 5.0116044044494625, + 4.996154860655467, + 4.9798126777013145, + 4.965713342030843, + 4.954478104909261, + 4.943256398042043, + 4.921875250339508, + 4.914606960614522, + 4.899503707885742, + 4.893172804514567, + 4.887858029206594, + 4.88621164560318, + 4.88164013226827 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "aft-nloglik": [ + 1.7734065099076717, + 1.6197563711867196, + 1.4976458711955158, + 1.3925457275547937, + 1.3060255839984132, + 1.2423128446853335, + 1.1810616951872548, + 1.1322734221484718, + 1.0842894627997242, + 1.051445002718269, + 1.0178983639780095, + 0.996230571710108, + 0.974674918964329, + 0.9576461062084315, + 0.947507673646346, + 0.9303152329498858, + 0.921520709098253, + 0.9122261276779752, + 0.9023172283725974, + 0.8986914384873738, + 0.8955653694450139, + 0.8963714056732957, + 0.8929962766000903, + 0.891572775275496 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "rmse": [ + 1.2325364259370402, + 1.3162893835804081, + 1.3915442471106123, + 1.4624127026649032 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "l2": [ + 1.4339700264041833, + 1.6320412485288422, + 1.8262533605839342, + 2.014177389235853 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "binary_logloss": [ + 0.6213523371820071, + 0.5486967349282297, + 0.4915110010453842, + 0.43862033990901683, + 0.3964246722120901, + 0.3564900332451924, + 0.3245027618622304, + 0.29359040565025213, + 0.2688830086303527, + 0.2445157370328498, + 0.22516882732980115, + 0.20569010040309055, + 0.19038254133236984, + 0.17463491065912654, + 0.162423553254113, + 0.1495711180099389, + 0.13976376723694678, + 0.1291873115752735, + 0.12126508879262181, + 0.11237800372659577, + 0.10595621900413515, + 0.09821247197895472, + 0.09120678856312132, + 0.08644717145310317 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "multi_logloss": [ + 1.1023683272199676, + 1.1083206450932424, + 1.1135855303108044, + 1.1227674358376611 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.20262679884781276, + 0.18336815029756953, + 0.16603536660235063, + 0.15043586127665368, + 0.14190466230571763, + 0.13530027920953328, + 0.1326058509954632, + 0.1301808656028002, + 0.1250262083326152, + 0.12332935379671887, + 0.12171725754813778, + 0.12225176419036404, + 0.12273282016836767, + 0.12088695174835097, + 0.11905326646563896, + 0.11740294971119818, + 0.11479637405602891, + 0.11245045596637657, + 0.11033912968568946, + 0.10843893603307106, + 0.10672876174571448, + 0.10518960488709357, + 0.10380436371433475, + 0.10255764665885182 + ] + } + } + }, + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.46272389098477074, + 0.43479768981829464, + 0.3986126022112219, + 0.36609509986929833, + 0.33930495582199666, + 0.3144555217749054, + 0.29209103113252316, + 0.27190816449930755, + 0.2544210185526883, + 0.24448489405418553, + 0.2362546523105289, + 0.22958138563753963, + 0.22357544563184942, + 0.21817009962672806, + 0.21551390001197354, + 0.2108689231161251, + 0.20851239626691334, + 0.20534328627993131, + 0.20334297432610757, + 0.201758284996264, + 0.20040193099322848, + 0.19910710838821022, + 0.19794176804369384, + 0.1968929617336291 + ] + } + } + }, + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.2004767091707326, + 0.19540910000011733, + 0.19084825174656356, + 0.18674348831836524, + 0.18304920123298668, + 0.17972434285614594, + 0.17673197031698937, + 0.1740388350317484, + 0.17161501327503154, + 0.1694335736939864, + 0.16747027807104573, + 0.16570331201039915, + 0.16411304255581718, + 0.16268180004669344, + 0.1613936817884821, + 0.16023437535609186, + 0.15919099956694066, + 0.15825196135670458, + 0.15741825212500785, + 0.15573088588850859, + 0.15421225627565932, + 0.15236331115894294, + 0.15069926055389823, + 0.14920161500935797 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "l2": [ + 1.1269352241178014, + 0.970516662096614, + 0.8437271876246057, + 0.7483928680327593, + 0.6637159108764034, + 0.5952755403219501, + 0.5550565798651889, + 0.5086098288610988, + 0.4699782302725713, + 0.44071740393315234, + 0.4174010833758155, + 0.3966304180591717, + 0.38117600995581, + 0.36892336790098323, + 0.3592485626591904, + 0.350690544891079, + 0.34359265582790927, + 0.3388407052815085, + 0.3369792823964243, + 0.33408013455137975, + 0.33203610998234045, + 0.33025599401759925, + 0.3301855685915053, + 0.3292099923879576 + ] + } + } + }, + { + "selected_rounds": 24, + "history": { + "validation": { + "l2": [ + 0.929684662744313, + 0.7842368592991047, + 0.6887280189598156, + 0.5762789804831753, + 0.48710590835495304, + 0.41069661450118644, + 0.3500430746877536, + 0.29961277570520006, + 0.25878872159908917, + 0.2274413162414056, + 0.20046026605783906, + 0.1802538249980812, + 0.1677717869196845, + 0.15229141073088842, + 0.1398530462382, + 0.1302888480514843, + 0.12257062617509913, + 0.11635022322834816, + 0.11108294947551058, + 0.10690852076653243, + 0.10361423842821876, + 0.1017766284091565, + 0.0995834653998774, + 0.09788080300516024 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 2.220446049250313e-16, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "poisson": [ + 0.5005120682856539, + 0.422793601689382, + 0.34580249019092385, + 0.290426369063737, + 0.24331593445585753, + 0.20297940161932224, + 0.1479531677784868, + 0.09972843745070623, + 0.05763817628021955, + 0.020701224085593377, + -0.011864898761765937, + -0.057322727023509844, + -0.0982475089257662, + -0.13525515173003388, + -0.16884838830065016, + -0.199443210906951, + -0.22738795117013721, + -0.2529773198588645, + -0.27646294758689766, + -0.29806142942063235, + -0.31672629630194105, + -0.34545249292066094, + -0.3713268060116866, + -0.394710227632587 + ] + } + } + } + ], + "exposure_doubling_max_abs_error": 1.7763568394002505e-15 + }, + { + "library": "lightgbm", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "gamma": [ + 1.5572463427516845, + 1.4790847906375282, + 1.4208560534542, + 1.3718738435738709, + 1.3284338373910762, + 1.291559985335919, + 1.2597323091754626, + 1.2299014398363803, + 1.2046453972127822, + 1.1841482321865366, + 1.1665933324277225, + 1.152620605189197, + 1.1406934096617745, + 1.1311367356689008, + 1.122463128523127, + 1.115715668854146, + 1.1095942007094153, + 1.1049684229027839, + 1.1006553581113887, + 1.0975158065819997, + 1.0954517411852744, + 1.0936720854384978, + 1.0919630295314093, + 1.0904683663383878 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 22, + "history": { + "validation": { + "tweedie": [ + 5.607787186534998, + 5.509538807869294, + 5.428697572677188, + 5.34550429435114, + 5.273467998541048, + 5.212112972295268, + 5.159684193358059, + 5.115821953277433, + 5.086649656958025, + 5.053047124181921, + 5.023873475858671, + 5.001380388206773, + 4.979656341299342, + 4.960872260315704, + 4.949660973306719, + 4.935165462008523, + 4.922809967014516, + 4.915788033975601, + 4.906080205374078, + 4.909411616047705, + 4.905959803551567, + 4.897560788539666, + 4.901267775115338, + 4.898782329247837 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "l2": [ + 1.51742705964158, + 1.7145253260664564, + 1.9013897600772813, + 2.0881430812946595 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "learn": { + "RMSE": [ + 1.012978198206559, + 0.9638517146626504, + 0.918770729350706, + 0.8812063908365043 + ] + }, + "validation": { + "RMSE": [ + 1.2565735750613158, + 1.3151692365662933, + 1.3648672921346283, + 1.3899750915034053 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "Logloss": [ + 0.6219704945882162, + 0.561628892686632, + 0.5083741929796007, + 0.4696956634521484, + 0.4294425964355469, + 0.39045291476779515, + 0.36120088365342884, + 0.333604007297092, + 0.30612419976128474, + 0.2881415049235026, + 0.2686185201009115, + 0.2501450856526693, + 0.2337204827202691, + 0.21997019449869792, + 0.20522193908691405, + 0.19628673129611546, + 0.18597842322455513, + 0.17601354387071397, + 0.16649158265855576, + 0.1606853167215983, + 0.1525210910373264, + 0.14609443876478406, + 0.14049678378634983, + 0.1334196514553494 + ] + }, + "validation": { + "Logloss": [ + 0.6110025365276788, + 0.548011164566562, + 0.4929916362186898, + 0.4519312493425164, + 0.4097429817307997, + 0.3702728367482893, + 0.34000206682735373, + 0.3124483559643589, + 0.28322176823640866, + 0.2606445644828456, + 0.24009714646247338, + 0.2217773070684931, + 0.20508254736425566, + 0.18985914597466846, + 0.17475723343897934, + 0.16377955391405397, + 0.1521867496431274, + 0.14213228058945201, + 0.13249683015636232, + 0.12463454520637564, + 0.11629010878308264, + 0.10949412901085756, + 0.10414688259153193, + 0.097059226994506 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 3, + "history": { + "learn": { + "MultiClass": [ + 1.0929380335149894, + 1.0894810901716065, + 1.084618211478942, + 1.0808856236425897, + 1.0662837720490004, + 1.0599046776994747 + ] + }, + "validation": { + "MultiClass": [ + 1.094600839854673, + 1.0939455114552343, + 1.0910410345850077, + 1.0915626854403928, + 1.1006849637538019, + 1.1061757178910294 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "MultiQuantile:alpha=0.1,0.5,0.9": [ + 0.23795171446057345, + 0.22486282054026685, + 0.20700150121524433, + 0.1969311916477969, + 0.18502166462821842, + 0.17578967316917882, + 0.16694645064868566, + 0.16062172308677966, + 0.1553732939565879, + 0.14866641698088479, + 0.14346283452404374, + 0.13768243393514387, + 0.13151083874630615, + 0.12687066929868346, + 0.12141076967964302, + 0.11742501508776022, + 0.11351216678447733, + 0.10863012674418217, + 0.10468313310966468, + 0.10143105766651851, + 0.09853063251110565, + 0.09474771915460732, + 0.09355363534804637, + 0.09032813021602075 + ] + }, + "validation": { + "MultiQuantile:alpha=0.1,0.5,0.9": [ + 0.2683737281615697, + 0.25505476270747607, + 0.24047204213131337, + 0.22871319164658474, + 0.21574672513842347, + 0.20143773927084455, + 0.19013389513333112, + 0.1829532674981523, + 0.17675633271104613, + 0.16998474623192375, + 0.1623963856542065, + 0.15641688839207354, + 0.14924082173419387, + 0.14420181216398478, + 0.13748433549612987, + 0.1330885746444381, + 0.128560208839024, + 0.1211921860558859, + 0.11654170600480954, + 0.11329442192260186, + 0.11190234244550304, + 0.10581998514947175, + 0.10553367005359486, + 0.10273571400080492 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "MultiRMSE": [ + 1.3476339802443345, + 1.309744554214489, + 1.2658099564025624, + 1.231394012370624, + 1.191957945764094, + 1.1626336760012543, + 1.10335249853103, + 1.0550481788939579, + 1.009435172170512, + 0.9797707947201433, + 0.9609696430188169, + 0.9358134926604141, + 0.9163883799896175, + 0.8954978668361525, + 0.8766056170772891, + 0.8402019631230708, + 0.8100933285776538, + 0.7794219581958013, + 0.7497878749451352, + 0.7484091179488189, + 0.7215482493782556, + 0.6954996436425271, + 0.6773520767647154, + 0.6502858328656659 + ] + }, + "validation": { + "MultiRMSE": [ + 1.5062551544719014, + 1.467509613588526, + 1.420735845251786, + 1.382446119589852, + 1.3318172120863434, + 1.2963309971110475, + 1.2256238927245398, + 1.169220069069057, + 1.1191704244534972, + 1.0934692197618638, + 1.0671229330906546, + 1.0416340084196054, + 1.0212820460422947, + 0.9938222196112297, + 0.9775749177381189, + 0.9371305984666112, + 0.9026292629375788, + 0.8685366852668811, + 0.8430986495989781, + 0.8438804597506208, + 0.8115637020131494, + 0.7828797348091495, + 0.7612980198858241, + 0.7333432552124127 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "learn": { + "Poisson": [ + 0.34115288266295785, + 0.17163258586769625, + 0.06935736559140565, + 0.006976786388112638 + ] + }, + "validation": { + "Poisson": [ + 0.5436233691285653, + 0.5441104842227786, + 0.5455787764793082, + 0.5476343151481607 + ] + } + } + } + ], + "exposure_doubling_max_abs_error": 8.881784197001252e-16 + }, + { + "library": "catboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "Tweedie:variance_power=1.5": [ + 5.395901885940239, + 5.31438091292526, + 5.239908625260069, + 5.1746406281609625, + 5.098447000871545, + 5.036321793511357, + 4.991825238806246, + 4.952675599797405, + 4.877352841234589, + 4.844477487052761, + 4.818065467823365, + 4.774566971038388, + 4.752227005776371, + 4.706654125604416, + 4.672446385175128, + 4.636186462188453, + 4.620028683028504, + 4.5905200903852235, + 4.565784184556538, + 4.557785490821003, + 4.538361179212804, + 4.528709929584562, + 4.508661048137493, + 4.4909689402950335 + ] + }, + "validation": { + "Tweedie:variance_power=1.5": [ + 5.745467801742013, + 5.726303797360546, + 5.709370680140718, + 5.695808670766458, + 5.606172101819614, + 5.57325887811127, + 5.564462717112858, + 5.557517215511638, + 5.445828590546447, + 5.378909156432513, + 5.375111649815649, + 5.328188702665831, + 5.27958032934185, + 5.214390941693139, + 5.183009137942778, + 5.147350032871332, + 5.133999953244005, + 5.091861744494804, + 5.063682960409003, + 5.051001374020511, + 5.024043987819706, + 5.0075000446987055, + 4.980631458052991, + 4.958488095496281 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 15, + "history": { + "learn": { + "SurvivalAft:dist=Normal;scale=1": [ + 1.219789320155794, + 1.1706216598129104, + 1.1332653679855706, + 1.0841168178607268, + 1.0472706050620793, + 1.0009581504591332, + 0.9695527762048747, + 0.9431649422721458, + 0.9135642680224603, + 0.9062331585689414, + 0.8867602462247459, + 0.847795635730943, + 0.8377684694081913, + 0.8348016509081654, + 0.7927069627728359, + 0.7898133068296669, + 0.7653165971901826, + 0.7523044359822811 + ] + }, + "validation": { + "SurvivalAft:dist=Normal;scale=1": [ + 1.3955753705667415, + 1.321899973100922, + 1.2550588127495719, + 1.175831253626942, + 1.0981945525326349, + 1.054609839279401, + 1.0294327994651467, + 0.9906220246460042, + 0.9560634013735984, + 0.9563624908333638, + 0.9248062050366707, + 0.8911395684636398, + 0.8826435814936511, + 0.894168569369036, + 0.8714135782826808, + 0.8754711344066806, + 0.8915317046794049, + 0.8953527579569182 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 23, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.4437337880647023, + 1.388591175136111, + 1.3394736509750325, + 1.3148899503399802, + 1.2525780433181535, + 1.1296700568623643, + 1.0583881347139599, + 1.0031424494938346, + 0.9333272805319993, + 0.8994600004249895, + 0.8447431047893572, + 0.7790904947401334, + 0.7396515125701246, + 0.7182874646381202, + 0.6552904287415786, + 0.6313427199690579, + 0.6180207983132339, + 0.5424822981927696, + 0.5135235276295191, + 0.4807097496726043, + 0.45048730775215434, + 0.39041639157722957, + 0.37291975803678706, + 0.3619668531463122 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.541215145829388, + 1.4527687196646966, + 1.4125794877324218, + 1.3834051632580109, + 1.3111479061082207, + 1.216929760923946, + 1.166293660558661, + 1.1064114362896835, + 1.035247449425124, + 1.0197575864610606, + 0.9619242000356178, + 0.9146876644295338, + 0.88904786602617, + 0.887673090554584, + 0.8081619845834918, + 0.8079486890282249, + 0.8126852591446043, + 0.7355369003249014, + 0.7126861751971094, + 0.6966942887634755, + 0.6893168889951754, + 0.6344391606556814, + 0.6312624987718344, + 0.6365117046715719 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "learn": { + "RMSE": [ + 1.019097744399966, + 0.9720372824809729, + 0.9281210336558733, + 0.8900834723731033 + ] + }, + "validation": { + "RMSE": [ + 1.2260142676069548, + 1.2848601534469415, + 1.335108608040352, + 1.3590492444945887 + ] + } + } + } + ] + }, + { + "library": "ngboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "train": { + "LOGSCORE": [ + 1.4711392929040266, + 1.3111732440489416, + 1.2245807802179178, + 1.089387298688345 + ] + }, + "val": { + "LOGSCORE": [ + 1.6596083297932713, + 1.6718481027878496, + 1.747947079640107, + 1.871604884969043 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + } + ], + "seed": 41, + "threads": 2, + "maximum_rounds": 24, + "patience": 3, + "source_sha": "f9dee9c901c035052a5a7d8bde91e11921e4cda3", + "dirty": true, + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "source_hashes": { + "early_stopping_smoke.py": "068db4dd7f7d7e42a72ed1fda88f1f54da485e798aaa51b1bc3cbd907b0dfc54", + "worker_smoke.py": "c140883bb7adf5832d44decbb2a31376770f162642bbce3c4808ccb84d39ee55", + "baseline_worker.py": "ba20c5487cffcd67beb9f69abbcf4454b20034c1bf815d0a51b082c8e3ee7092" + } +} diff --git a/benchmarks/v1/evidence/expectile-043/README.md b/benchmarks/v1/evidence/expectile-043/README.md new file mode 100644 index 0000000..1ceb802 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/README.md @@ -0,0 +1,21 @@ +# Installed D1 expectile development evidence + +Reproduce from the repository with cached build/NumPy dependencies: + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python examples/v1_extensions/verify.py /tmp/openboost-v1-expectile-043 +``` + +`manifest.json` records parent revision and dirty state, exact commands, source +and wheel SHA256s, reference hashes, environment and artifact hashes. This is a +CPU correctness fixture, not a timing or quality benchmark. No GPU was used. +`expectile-expected.json` is generated separately from independent reference +formulas and exhaustive trees. `expectile-checks.json` records installed two-round +comparison; `expectile-model.json` contains the plugin-independent raw model. +Other JSON files rerun D2/D3/D4 and mixed scheduling checks. All nine models retain +exact raw predictions after uninstalling all four training plugins. + +Synthetic fixtures and versioned source define data; no split or quality search +is implied. The developer-authored checks do not close E5 or establish independent +adoption. Prior sprint evidence is retained unchanged. See +[Sprint 043](../../../../v1-sprints/043-expectile-extension.md). diff --git a/benchmarks/v1/evidence/expectile-043/checks.json b/benchmarks/v1/evidence/expectile-043/checks.json new file mode 100644 index 0000000..2213353 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/checks.json @@ -0,0 +1,93 @@ +{ + "values": [ + [ + 0.0, + "a" + ], + [ + 1.0, + "b" + ], + [ + 2.0, + null + ], + [ + 3.0, + "a" + ], + [ + null, + "b" + ], + [ + 5.0, + "a" + ] + ], + "row_ids": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "names": [ + "x", + "category" + ], + "kinds": [ + "numeric", + "categorical" + ], + "predictions": { + "d2": [ + [ + 1.5801874999999999 + ], + [ + 1.5801874999999999 + ], + [ + 1.7251249999999998 + ], + [ + 2.106375 + ], + [ + 2.7743 + ], + [ + 2.7743 + ] + ], + "d3": [ + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.164000000000001 + ] + ] + }, + "d2_cuts": [ + 1, + 1, + 1 + ], + "d3_max_absolute_error": 8.881784197001252e-16, + "rounds": 3 +} diff --git a/benchmarks/v1/evidence/expectile-043/d2.json b/benchmarks/v1/evidence/expectile-043/d2.json new file mode 100644 index 0000000..b6d4143 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/d2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [2.25], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.0], [2.0]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [126, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.2874999999999996], [1.5249999999999997]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, 0, -1, -1, -1], "threshold": [190, 63, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.008472222222222017], [-1.7332499999999995], [1.718], [-2.410625], [-0.9612499999999998]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/d3.json b/benchmarks/v1/evidence/expectile-043/d3.json new file mode 100644 index 0000000..5efc3b8 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/d3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [4.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.76], [3.76]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/expectile-checks.json b/benchmarks/v1/evidence/expectile-043/expectile-checks.json new file mode 100644 index 0000000..5790ca1 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/expectile-checks.json @@ -0,0 +1,46 @@ +{ + "passed": true, + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "raw": [ + [ + 4.041211449676824 + ], + [ + 4.041211449676824 + ], + [ + 4.041211449676824 + ], + [ + 4.041211449676824 + ], + [ + 5.3587241118669695 + ], + [ + 5.3587241118669695 + ] + ], + "rounds": 2, + "reason": "budget", + "max_abs_error": 0.0 +} diff --git a/benchmarks/v1/evidence/expectile-043/expectile-expected.json b/benchmarks/v1/evidence/expectile-043/expectile-expected.json new file mode 100644 index 0000000..f9c679c --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/expectile-expected.json @@ -0,0 +1,103 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "target": [ + -3.0, + 0.0, + 2.0, + 2.0, + 90.0, + 7.0 + ], + "weight": [ + 2.0, + 1.0, + 3.0, + 1.0, + 0.0, + 2.0 + ], + "offset": [ + 1.0, + -1.0, + 0.0, + 2.0, + 1.0, + -2.0 + ], + "base": 4.733333333333333, + "trace": [ + { + "loss": 7.931851851851851, + "gradient": [ + 3.4933333333333327, + 1.493333333333333, + 1.093333333333333, + 1.8933333333333329, + -134.82666666666668, + -6.826666666666667 + ], + "curvature": [ + 0.3999999999999999, + 0.3999999999999999, + 0.3999999999999999, + 0.3999999999999999, + 1.6, + 1.6 + ], + "raw": [ + 4.374035087719299, + 4.374035087719299, + 4.374035087719299, + 4.374035087719299, + 5.058412698412699, + 5.058412698412699 + ] + }, + { + "loss": 6.932494099197992, + "gradient": [ + 3.3496140350877193, + 1.349614035087719, + 0.9496140350877192, + 1.749614035087719, + -134.3065396825397, + -6.3065396825396824 + ], + "curvature": [ + 0.3999999999999999, + 0.3999999999999999, + 0.3999999999999999, + 0.3999999999999999, + 1.6, + 1.6 + ], + "raw": [ + 4.041211449676824, + 4.041211449676824, + 4.041211449676824, + 4.041211449676824, + 5.3587241118669695, + 5.3587241118669695 + ] + } + ] +} diff --git a/benchmarks/v1/evidence/expectile-043/expectile-model.json b/benchmarks/v1/evidence/expectile-043/expectile-model.json new file mode 100644 index 0000000..ffedb56 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/expectile-model.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [4.733333333333334], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-3.592982456140351], [3.25079365079365]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.004888411504953132], [-3.3282363804247472], [3.003114134542706]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/manifest.json b/benchmarks/v1/evidence/expectile-043/manifest.json new file mode 100644 index 0000000..95d7413 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/manifest.json @@ -0,0 +1,251 @@ +{ + "schema": "openboost-v1-development-extensions-v1", + "passed": true, + "claim": "repository-authored D1/D2/D3/D4 and D5 scheduling development checks only", + "commit": "b7fdb60bfa47b7d607f3b47e6286b2ee247ffd9f", + "dirty": true, + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "threads": 1, + "commands": [ + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/cohort_splits" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/penalized_leaves" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/ordered_updates" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/expectile" + }, + { + "argv": [ + "uv", + "venv", + "--python", + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "uv", + "pip", + "install", + "--offline", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "numpy==2.3.5", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels/ob_cohort_splits-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels/ob_expectile-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels/ob_ordered_updates-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels/ob_penalized_leaves-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/wheels/openboost-1.0.0.dev0-py3-none-any.whl" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/checks.py", + "/private/tmp/openboost-v1-expectile-043" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "examples.v1_extensions.ordered_oracle", + "/private/tmp/openboost-v1-expectile-043/ordered-expected.json" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/ordered_checks.py", + "/private/tmp/openboost-v1-expectile-043" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/scheduler_checks.py", + "/private/tmp/openboost-v1-expectile-043" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "examples.v1_extensions.expectile_oracle", + "/private/tmp/openboost-v1-expectile-043/expectile-expected.json" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/expectile_checks.py", + "/private/tmp/openboost-v1-expectile-043" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "-I", + "-c", + "import json,importlib.metadata as m; print(json.dumps({n:m.version(n) for n in ['openboost','numpy','ob-cohort-splits','ob-penalized-leaves','ob-ordered-updates','ob-expectile']}))" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "uv", + "pip", + "uninstall", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "ob-cohort-splits", + "ob-penalized-leaves", + "ob-ordered-updates", + "ob-expectile" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys/core_inference.py", + "/private/tmp/openboost-v1-expectile-043" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-89ado3ys" + } + ], + "sources": { + "examples/v1_extensions/checks.py": "47556dd3e9b5681d1bddb0ae8e086748fe273ddf39e25ab35b5e6a27d34e985b", + "examples/v1_extensions/cohort_splits/pyproject.toml": "d29f65c7783437a6220ec2dcfc6c4497792e804226c2b8f55d33f25560267245", + "examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py": "013730f77db77255d3be58ce16fe4883e55e4dcea80ac757a681fa5444e82178", + "examples/v1_extensions/core_inference.py": "e99d5e6b1741f9fddf07be46fd4a03f45631cad187a89d538d8e056b865ea7e2", + "examples/v1_extensions/expectile/pyproject.toml": "792c06b65fd6426d859c8ca85d5383266e84512798ae04935890fb4b3c004355", + "examples/v1_extensions/expectile/src/ob_expectile/__init__.py": "29b4a91bc5b9c17618be0c8373f6a7cb61033ca7fda3bf2bc987ecfa123865f1", + "examples/v1_extensions/expectile_checks.py": "91e92d26036009221aa30dffb6c3e4f0973d2384a749397fe6ff8966c752f40b", + "examples/v1_extensions/expectile_oracle.py": "d759d43c888fd5233c04a3335cac917ae663aee2f81f6ed0126ed4acdda25006", + "examples/v1_extensions/ordered_checks.py": "f431ffcfa1f578ce1ca1d9dece98efcc3ca239d881ac62ff257b53c7e6e265f9", + "examples/v1_extensions/ordered_oracle.py": "f225d630f3ec84645557b1ccc99839864393a1b7c1032ce55f346d2ff2641fe1", + "examples/v1_extensions/ordered_updates/pyproject.toml": "6e3558622d940f91f6454483b962957d218001575dcce5e46172be7769c2c28e", + "examples/v1_extensions/ordered_updates/src/ob_ordered_updates/__init__.py": "4b71fcfba6ccb207f98f10ed765fe0eaa6f8097e678972efd831927ddc8fdcf2", + "examples/v1_extensions/penalized_leaves/pyproject.toml": "e00f1e32176ca2f5295e233c2421c39f729babfea0d283228896b53fd914e4fe", + "examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py": "54208a6c52fe2acacc539f25428f31e28303a286c50dd5892b403c08e1fa0be2", + "examples/v1_extensions/scheduler_checks.py": "ea8bf14b9f046e2b65c04bfe096f82e054c219f0b440315ef65cfa0ec00f0599", + "examples/v1_extensions/verify.py": "d6e55121d109870b41b1530f5a7095be7709ce42fbe2a3c87a97c6f95d7242b2" + }, + "reference_sources": { + "tests/v1/reference/__init__.py": "a6b57448d0bd41863e7d88c7f79a0429652b399ffa8a45478b948cd38f7c57c7", + "tests/v1/reference/author.py": "155d6b2a02baaed5357a1032227e263a6c53d5ee59e6cd6c700f24b13d487dac", + "tests/v1/reference/classification.py": "f3a9cfd5610cef1e2b05cf8e4b2eaf0b77eee5ee12d57d8bdf1c0195613900ad", + "tests/v1/reference/coupled.py": "691c9bd8dacc645d824d7c75bd909590095b07844b923a6929ac79bb8f7e5a8b", + "tests/v1/reference/data.py": "10e4380b35fa156cc6a1c025bca172733289c032b6c4c06421e11e9d430be072", + "tests/v1/reference/integration.py": "62ba6d983b5b457690272d4e28e5b179f6732e0a78ab9faa3ea176fa9119d27e", + "tests/v1/reference/mixed.py": "70dfce643f1b4b6e8d6014c642efbae0d10d32b7269706867af2c590ae84641e", + "tests/v1/reference/positive.py": "d55c40f5dfd96aa58e4ccdb02cb9f7691352e82966bccb4e13d79e278c6ba190", + "tests/v1/reference/quantile.py": "6e05e309c06494cde68d4c11496b7d7aad7f0c4958e0688562fc79451855079a", + "tests/v1/reference/ranking.py": "a2d23b4ba1595b8aac90cdc32b0e41fa90fc42e11a999019d53405eb62199a0c", + "tests/v1/reference/runs.py": "010c43f3752bd22d4959ec89ea40de8f7705ec116dfb53ccae7a3e85c3c93c20", + "tests/v1/reference/scalar.py": "5d78f361abb2bdad2532e322f2b2138ee7c5bd6fed55d6106a63ba707cadb6b1", + "tests/v1/reference/survival.py": "1b4d6704edc2b58c2ddb39b6e4f00de9d205c54250c2a95a8a0458cdab01f46a", + "tests/v1/reference/tree.py": "d8afa1a3ea9447f2fa420317e972e5b2e42540888ce42106d69ccd61aae21874", + "tests/v1/reference/vector.py": "1294d8c452df385c0c31f15c9dd57e11c09ae17bef62d0fa8f5754c4e7efe12f" + }, + "wheels": { + "ob_cohort_splits-0.1.0-py3-none-any.whl": "2e9ebb56cd551f6ebeb150e866c5107f508d62069c45841ffdf0e51c6305141b", + "ob_expectile-0.1.0-py3-none-any.whl": "9b8dd342c496e013c66597107cd1de0fa557d9cd665f5bd3c48e00f81c7dd43f", + "ob_ordered_updates-0.1.0-py3-none-any.whl": "9a058e89931a5c820350f6b34ccc541c0be86e0becc06d1c14a7b1f943b6c310", + "ob_penalized_leaves-0.1.0-py3-none-any.whl": "e7723fc496bcaf908b52f6ed11483833bbdb1f51da7c5e02eead10d27ed90236", + "openboost-1.0.0.dev0-py3-none-any.whl": "eb7246deab69de5cbdf7626bcc9993e09b605c73e3d65b74c996d3abfb1e3902" + }, + "versions": { + "openboost": "1.0.0.dev0", + "numpy": "2.3.5", + "ob-cohort-splits": "0.1.0", + "ob-penalized-leaves": "0.1.0", + "ob-ordered-updates": "0.1.0", + "ob-expectile": "0.1.0" + }, + "plugin_free_inference": true, + "artifacts": { + "checks.json": "184006082086b9b5593ea7a7e18c9fdbd34ecae7beb52fa72ac64b883ffd045c", + "d2.json": "facc6da0ae50d74322f4552cf70a11c749f480a7dfb7d102579cec825e88e657", + "d3.json": "f701c628ebbf191056b72bb2a1d3b92d33385189423fa53bc609b33107f803f2", + "expectile-checks.json": "b7f106e38303f08cf254018cedc065ed37530dc1eed1d810716309289ab8693b", + "expectile-expected.json": "2139e426ce228cdd3916889e46226345429a6491dfb3da092836070fda9ab07e", + "expectile-model.json": "b729b8297043234f77a1284cdb71c1610407472a5356a3ded499ace5ca73c669", + "ordered-0.json": "325017af3b8f819664eff01df2ac49cfeadc7b042bba416cdd378c3b30016a04", + "ordered-1.json": "03cc3d28f8adc851a28f9596af22376026d8b81b3cce5a126f3ed1c2ea74576e", + "ordered-2.json": "d8b26886a41ee3b194c6983fc2f8b905b741133ef228ffa28fe8f604fd6bba2f", + "ordered-3.json": "35a29588374bfb82a559c42c3a91b67b8fed43a1778610c6078b1c1eda2103d9", + "ordered-4.json": "7ca0acb133c1c034f67f116b58cd4c836ae8c8e67ba31d9656d15c01d90c9de9", + "ordered-5.json": "22067f5ed6ead94e721bf03d652edbe9b3faa3c8c62f08ce85b8f57d12079ac2", + "ordered-checks.json": "998b3a0e2df12fa149c26099753687735d1bcbd7060db8f426c4c87cde9659d0", + "ordered-expected.json": "68f858fd4edcc561b16c492b146f3f28cc079212797cff6334493893cd499926", + "scheduler-checks.json": "7840dc5e6d1d7bbbecafb0ab883078bbb3868e4afbdc603c89e652299f60df1e" + } +} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-0.json b/benchmarks/v1/evidence/expectile-043/ordered-0.json new file mode 100644 index 0000000..3f722f0 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-0.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.0], [-1.55], [1.55], [-1.734375], [-0.40625]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.05211814894448784], [0.537839591477717], [-0.2975117307047917]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00656250000000015], [-1.4378125000000002], [1.4259999999999997], [-1.6042968750000002], [-0.38593750000000004], [2.01625], [0.7743749999999998]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.11905690030262675], [0.37209581591266455], [-0.3125427929230907]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0029869791666665454], [-1.3338359375], [1.3392124999999997], [-1.483974609375], [-0.36664062500000005], [1.9154375], [0.7162968749999997]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.1674487489868146], [0.2577416257105913], [-0.3257519454304436]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-1.json b/benchmarks/v1/evidence/expectile-043/ordered-1.json new file mode 100644 index 0000000..7e2823f --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-1.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-2.4671622769447922e-17], [0.617670054730258], [-0.26471573774153917], [-0.11415168100078188], [-0.30211102423768577]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.0], [-1.55], [1.55], [-1.734375], [-0.40624999999999994]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.08185660294329283], [0.4366019642073911], [-0.29235933130168695]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00656250000000015], [-1.4378125000000002], [1.4259999999999997], [-1.6042968750000002], [-0.38593750000000004], [2.01625], [0.7743749999999998]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.1386300959880271], [0.30287104064812337], [-0.3080405694052306]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0029869791666665454], [-1.3338359375], [1.3392124999999997], [-1.483974609375], [-0.36664062500000005], [1.9154375000000001], [0.7162968749999996]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-2.json b/benchmarks/v1/evidence/expectile-043/ordered-2.json new file mode 100644 index 0000000..208f9e2 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[1.2335811384723961e-17], [-0.31024237685691947], [0.31024237685691947], [-0.3471462079749804], [-0.08131352619233777]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.021532890322055052], [1.202805041760208], [-0.5431730197398742]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.006713870020361525], [-0.29772120406858105], [0.2856362380319303], [-0.3272827548559434], [-0.08973750045956581]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.14554489709520965], [0.7794770933518844], [-0.5211907648446487], [-0.2136889306782752], [-0.601453712375543]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.004724115047481874], [-0.28996494829505315], [0.2814615412095858]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.2124232464137005], [0.551922150051749], [-0.5096536668397931], [-0.2094904804283043], [-0.5878208453187278]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-3.json b/benchmarks/v1/evidence/expectile-043/ordered-3.json new file mode 100644 index 0000000..c6c645b --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-4.9343245538895844e-17], [1.235340109460516], [-0.5294314754830783], [-0.22830336200156376], [-0.6042220484753715]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0001705718713431972], [-0.2921712432158142], [0.29186421384739647]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.13678840055064134], [0.7962304698465822], [-0.5171124306422169], [-0.2224970798353251], [-0.5904591549979086]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.002177683397250046], [-0.28361181227925364], [0.28753164239430373]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.20521263860862954], [0.5620408713757129], [-0.5047194802292578], [-0.21693892691354133], [-0.5764439161728362]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.005841160761291825], [-0.2812088257653382], [0.29172291513566345]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-4.json b/benchmarks/v1/evidence/expectile-043/ordered-4.json new file mode 100644 index 0000000..27d6836 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-4.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.7901594196053434, 0.5413248546129181], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.7132126139527316], [-0.05361965178016352], [1.3374023568950804], [-0.09977238181478348], [0.06549563417915817]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5681877339281609], [-0.10366525878116846], [1.1264031798518581], [-0.16860988364095933], [0.07805662032899752]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6916677838393316], [-0.04950491988957833], [1.2945069308003752], [-0.0932129638142678], [0.06266362790458978]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5002017662617205], [-0.09741553384741679], [0.9977787131185136], [-0.1589450731540854], [0.07435131168962882]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6664451940126678], [-0.045701998802632156], [1.245303348025434], [-0.0871011118862839], [0.05994722676598742]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.44007587706961016], [-0.09152583965232537], [0.8836624183776237], [-0.14982120030930768], [0.07082780148780192]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-5.json b/benchmarks/v1/evidence/expectile-043/ordered-5.json new file mode 100644 index 0000000..6bb1eaa --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-5.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.7901594196053434, 0.5413248546129181], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5925937328319512], [-0.10515689902826628], [1.1718256181257787], [-0.17122868934608548], [0.07956513112150526]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.742570059559497], [-0.05001600877035171], [1.386642115977446], [-0.09451013935344513], [0.063980256781011]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5195503218044729], [-0.09881503450919896], [1.0340056137572504], [-0.16141157957113175], [0.0757855728692661]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.7136045762384516], [-0.046162405522191106], [1.330650642751404], [-0.08830777468338843], [0.0612095355612991]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.4554821173230488], [-0.09283829703588686], [0.9127061082173746], [-0.15214372865535286], [0.07219171472098855]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6819662495495865], [-0.04260017510397546], [1.2701394242932311], [-0.08252636533256741], [0.05855229290519618]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-checks.json b/benchmarks/v1/evidence/expectile-043/ordered-checks.json new file mode 100644 index 0000000..eae0b6e --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-checks.json @@ -0,0 +1,190 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "predictions": { + "ordered-0": [ + [ + 3.3302353515625, + 0.9210958818597373 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.6966171875000002, + 0.7107475316438076 + ], + [ + 4.1165671875, + 0.7107475316438076 + ], + [ + 4.36066875, + 0.9210958818597373 + ] + ], + "ordered-1": [ + [ + 3.3302353515625, + 0.9400424845082175 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.6966171875000002, + 0.7140770860551798 + ], + [ + 4.1165671875, + 0.7140770860551798 + ], + [ + 4.36066875, + 0.9400424845082175 + ] + ], + "ordered-2": [ + [ + 3.7160606088874024, + 1.0577486070660242 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7663984025053043, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 1.0577486070660242 + ] + ], + "ordered-3": [ + [ + 3.726800811873959, + 1.0636893236179212 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 1.0636893236179212 + ] + ], + "ordered-4": [ + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.8089700684903174, + 0.563648427963561 + ], + [ + 4.177880683177432, + 0.8421092857477177 + ], + [ + 4.177880683177432, + 0.8421092857477177 + ] + ], + "ordered-5": [ + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.8085336281300934, + 0.564079096484094 + ], + [ + 4.188902637907551, + 0.8531785886229586 + ], + [ + 4.188902637907551, + 0.8531785886229586 + ] + ] + }, + "versions": { + "ordered-0": 6, + "ordered-1": 6, + "ordered-2": 6, + "ordered-3": 6, + "ordered-4": 6, + "ordered-5": 6 + }, + "max_absolute_error": 2.220446049250313e-16, + "scheduler_status": "structural result accepted; all six cases match independent execution" +} diff --git a/benchmarks/v1/evidence/expectile-043/ordered-expected.json b/benchmarks/v1/evidence/expectile-043/ordered-expected.json new file mode 100644 index 0000000..12e7839 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/ordered-expected.json @@ -0,0 +1,2232 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "target": [ + 0.5, + 1.0, + 2.0, + 3.0, + 5.0, + 8.0 + ], + "weight": [ + 1, + 0, + 2, + 1, + 3, + 1 + ], + "structure": [ + 0.2, + 0.56, + 0.9199999999999999, + 1.28, + 1.64, + 2.0 + ], + "records": [ + { + "family": "normal", + "mode": "natural", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.771875, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.771875, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8581121376974118 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.771875, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.8581121376974118 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.6390625, + 0.8581121376974118 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.771875, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.8581121376974118 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.8581121376974118 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.73328125, + 0.774577005479161 + ], + [ + 4.0449375000000005, + 0.774577005479161 + ], + [ + 4.169125, + 0.8581121376974118 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.4786328125, + 0.8581121376974118 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.73328125, + 0.774577005479161 + ], + [ + 4.0449375000000005, + 0.774577005479161 + ], + [ + 4.169125, + 0.8581121376974118 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.8953217192886782 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.73328125, + 0.7433227261868519 + ], + [ + 4.0449375000000005, + 0.7433227261868519 + ], + [ + 4.169125, + 0.8953217192886782 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.4786328125, + 0.8953217192886782 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.73328125, + 0.7433227261868519 + ], + [ + 4.0449375000000005, + 0.7433227261868519 + ], + [ + 4.169125, + 0.8953217192886782 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.8953217192886782 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.6966171875000002, + 0.7433227261868519 + ], + [ + 4.1165671875, + 0.7433227261868519 + ], + [ + 4.36066875, + 0.8953217192886782 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.3302353515625, + 0.8953217192886782 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.6966171875000002, + 0.7433227261868519 + ], + [ + 4.1165671875, + 0.7433227261868519 + ], + [ + 4.36066875, + 0.8953217192886782 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.9210958818597373 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.6966171875000002, + 0.7107475316438076 + ], + [ + 4.1165671875, + 0.7107475316438076 + ], + [ + 4.36066875, + 0.9210958818597373 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "natural", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.8125, + 0.8660951840226659 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.8660951840226659 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8660951840226659 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.8660951840226659 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8660951840226659 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.771875, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.8660951840226659 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.6390625, + 0.8660951840226659 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.771875, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.8660951840226659 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.9097553804434051 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.771875, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.9097553804434051 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.6390625, + 0.9097553804434051 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.771875, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.9097553804434051 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.9097553804434051 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.73328125, + 0.7448811429957028 + ], + [ + 4.0449375000000005, + 0.7448811429957028 + ], + [ + 4.169125, + 0.9097553804434051 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.4786328125, + 0.9097553804434051 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.73328125, + 0.7448811429957028 + ], + [ + 4.0449375000000005, + 0.7448811429957028 + ], + [ + 4.169125, + 0.9097553804434051 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.9400424845082175 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.73328125, + 0.7140770860551798 + ], + [ + 4.0449375000000005, + 0.7140770860551798 + ], + [ + 4.169125, + 0.9400424845082175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.4786328125, + 0.9400424845082175 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.73328125, + 0.7140770860551798 + ], + [ + 4.0449375000000005, + 0.7140770860551798 + ], + [ + 4.169125, + 0.9400424845082175 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.9400424845082175 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.6966171875000002, + 0.7140770860551798 + ], + [ + 4.1165671875, + 0.7140770860551798 + ], + [ + 4.36066875, + 0.9400424845082175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "ordinary", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.804368647380766, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.804368647380766, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.777785379202502, + 0.924608682725661 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.804368647380766, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.924608682725661 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.777785379202502, + 0.924608682725661 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.804368647380766, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.924608682725661 + ] + ], + "raw_after": [ + [ + 3.7450571037169076, + 0.924608682725661 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7953948973348095, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.924608682725661 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7450571037169076, + 0.924608682725661 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7953948973348095, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.924608682725661 + ] + ], + "raw_after": [ + [ + 3.7450571037169076, + 1.0025563920608493 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7953948973348095, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 1.0025563920608493 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7450571037169076, + 1.0025563920608493 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7953948973348095, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 1.0025563920608493 + ] + ], + "raw_after": [ + [ + 3.7160606088874024, + 1.0025563920608493 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7663984025053043, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 1.0025563920608493 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7160606088874024, + 1.0025563920608493 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7663984025053043, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 1.0025563920608493 + ] + ], + "raw_after": [ + [ + 3.7160606088874024, + 1.0577486070660242 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7663984025053043, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 1.0577486070660242 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "ordinary", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.8125, + 0.9278621894956918 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.9278621894956918 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.9278621894956918 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.9278621894956918 + ] + ], + "raw_after": [ + [ + 3.7832828756784185, + 0.9278621894956918 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.9278621894956918 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7832828756784185, + 0.9278621894956918 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.9278621894956918 + ] + ], + "raw_after": [ + [ + 3.7832828756784185, + 1.00748523648035 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 1.00748523648035 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7832828756784185, + 1.00748523648035 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 1.00748523648035 + ] + ], + "raw_after": [ + [ + 3.754921694450493, + 1.00748523648035 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 1.00748523648035 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.754921694450493, + 1.00748523648035 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 1.00748523648035 + ] + ], + "raw_after": [ + [ + 3.754921694450493, + 1.0636893236179212 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 1.0636893236179212 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.754921694450493, + 1.0636893236179212 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 1.0636893236179212 + ] + ], + "raw_after": [ + [ + 3.726800811873959, + 1.0636893236179212 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 1.0636893236179212 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "formula", + "mode": "full", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.7967089830232594, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.7967089830232594, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.7967089830232594, + 0.5491305166458179 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.7967089830232594, + 0.5491305166458179 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ] + ], + "raw_after": [ + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.8029753458137185, + 0.5491305166458179 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.8029753458137185, + 0.5491305166458179 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ] + ], + "raw_after": [ + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.8029753458137185, + 0.5565656478147808 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.8029753458137185, + 0.5565656478147808 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ] + ], + "raw_after": [ + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.8089700684903174, + 0.5565656478147808 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.8089700684903174, + 0.5565656478147808 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ] + ], + "raw_after": [ + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.8089700684903174, + 0.563648427963561 + ], + [ + 4.177880683177432, + 0.8421092857477175 + ], + [ + 4.177880683177432, + 0.8421092857477175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "formula", + "mode": "full", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5492813677250686 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5492813677250686 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ] + ], + "raw_after": [ + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.7965574452834443, + 0.5492813677250686 + ], + [ + 3.928823631203088, + 0.658507416425496 + ], + [ + 3.928823631203088, + 0.658507416425496 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.7965574452834443, + 0.5492813677250686 + ], + [ + 3.928823631203088, + 0.658507416425496 + ], + [ + 3.928823631203088, + 0.658507416425496 + ] + ], + "raw_after": [ + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.7965574452834443, + 0.5568599250119952 + ], + [ + 3.928823631203088, + 0.761907977801221 + ], + [ + 3.928823631203088, + 0.761907977801221 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.7965574452834443, + 0.5568599250119952 + ], + [ + 3.928823631203088, + 0.761907977801221 + ], + [ + 3.928823631203088, + 0.761907977801221 + ] + ], + "raw_after": [ + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.802678398839574, + 0.5568599250119952 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.802678398839574, + 0.5568599250119952 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ] + ], + "raw_after": [ + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.802678398839574, + 0.564079096484094 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.802678398839574, + 0.564079096484094 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ] + ], + "raw_after": [ + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.8085336281300934, + 0.564079096484094 + ], + [ + 4.188902637907551, + 0.8531785886229585 + ], + [ + 4.188902637907551, + 0.8531785886229585 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + } + ] +} diff --git a/benchmarks/v1/evidence/expectile-043/scheduler-checks.json b/benchmarks/v1/evidence/expectile-043/scheduler-checks.json new file mode 100644 index 0000000..184b315 --- /dev/null +++ b/benchmarks/v1/evidence/expectile-043/scheduler-checks.json @@ -0,0 +1,1221 @@ +[ + { + "count": 1, + "failed_result_retained": true, + "reorder": true, + "regroup": true, + "retry": true, + "runs": { + "mixed-0": { + "state": "c18dfb158de427767f009c82051837099f40c5f6ae00a35c0807c23f7a0137c5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + } + } + }, + { + "count": 8, + "failed_result_retained": true, + "reorder": true, + "regroup": true, + "retry": true, + "runs": { + "mixed-0": { + "state": "c18dfb158de427767f009c82051837099f40c5f6ae00a35c0807c23f7a0137c5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-1": { + "state": "04c791bdea8df6eb4395fbbe334f81f2c8054b8a39803ed64c3f84cfc3886871", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-2": { + "state": "54d4dd48f53d93d8224c8d969e6b1207703d4d1ec6e487a5ec88e1ee13bae22c", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-3": { + "state": "9a1f5b9078162fec92e30e40b928cbc7493b0bc76d0892d9998bb44e2fbb1d20", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-4": { + "state": "b63dca8e05fd552d336652db6757d67aebcb98608c6da8b3466ee6c18f9a0e7a", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-5": { + "state": "7c229af07f459c011c04fa3e2ab83a6b405a0cb45061c03cdcdf45d58640f9db", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-6": { + "state": "eecaddbe0d79491fd01ee071c04f867a41651f7c4b0119f6f15518d28aadf6d5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-7": { + "state": "b14d4b28f8028e605e56293959eb76806ae88cb808e7817d31e45f36216d80bb", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + } + } + }, + { + "count": 32, + "failed_result_retained": true, + "reorder": true, + "regroup": true, + "retry": true, + "runs": { + "mixed-0": { + "state": "c18dfb158de427767f009c82051837099f40c5f6ae00a35c0807c23f7a0137c5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-1": { + "state": "04c791bdea8df6eb4395fbbe334f81f2c8054b8a39803ed64c3f84cfc3886871", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-2": { + "state": "54d4dd48f53d93d8224c8d969e6b1207703d4d1ec6e487a5ec88e1ee13bae22c", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-3": { + "state": "9a1f5b9078162fec92e30e40b928cbc7493b0bc76d0892d9998bb44e2fbb1d20", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-4": { + "state": "b63dca8e05fd552d336652db6757d67aebcb98608c6da8b3466ee6c18f9a0e7a", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-5": { + "state": "7c229af07f459c011c04fa3e2ab83a6b405a0cb45061c03cdcdf45d58640f9db", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-6": { + "state": "eecaddbe0d79491fd01ee071c04f867a41651f7c4b0119f6f15518d28aadf6d5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-7": { + "state": "b14d4b28f8028e605e56293959eb76806ae88cb808e7817d31e45f36216d80bb", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-8": { + "state": "7c61559e6fec81ed17a3c7fc4c22df08d76e58e9dae8960e2136646eab1edfb3", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-9": { + "state": "ed9eb234d34ec2c482216bbee1abb75b7bda985a862941bc04f8939c21c9aa11", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-10": { + "state": "0c265699464af63192c22ba556ef5bc766a42817c7f28f6ab503c0ded3f84836", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-11": { + "state": "e0ca68a0c1f0d07b0d691f70a20c0c5e02e84140661cc7e2b99da6c444cb83c4", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-12": { + "state": "5facc97d3c37592d39863e120f934dcb0a1d5fad7514bcef5d891bdd3b560e2d", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-13": { + "state": "55def2a9512221b80e733ab921551f938cb00215ee6c909fe0b29e4259d5852d", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-14": { + "state": "5e70de52d0ccbaa6b9e0839bc0e83def37f9fece1d8f89e45f8c42c1233cb730", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-15": { + "state": "72439bb18f44d98d5cb4e02a5535e2156b3be45f33f6e5fee76a498ad3121b70", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-16": { + "state": "65bef2f5bc48e53fc8417957b287f0ab74fa535a3050d04b6aa749502d178119", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-17": { + "state": "34638a96c6c01fc2b17eb1df956f1b8507147cba0540f553c1f57e46145a24e8", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-18": { + "state": "0dc94ccc783ffd3d2cb726f451d7e36fcf118f908f343228c032fb11cec3c481", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-19": { + "state": "3f6385aa76fd477099d55254c73e3a00525326da6ac57caa62e56dff91194f46", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-20": { + "state": "70370578452670373c4ce2d4585f1ef6596f4510b50296240bbdf111f1cc47c8", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-21": { + "state": "ed17199529e786cc3df74e52d92c1dbad768f580b668cb341ff10740208ee9f1", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-22": { + "state": "0a6b5cc5a34ce685016e09c5e30117c01cd7016259e17f4e33cc056ad5d569ef", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-23": { + "state": "a1a5c5c45de913eb1bb43c1bd483eb7f2d84204fecd98e1d178ff011345be154", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-24": { + "state": "b3dea81203e36fa498a7f29ec62f633410e3f2c7174eafa0a119227841af3d89", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-25": { + "state": "48a0471b3fc4b2d32448b675d2f310cd8703331ecdca9ae88a0385a8778779c4", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-26": { + "state": "29efc3b2909810f259468f3fc95e97e81ff93d8d75b8e4ed8e904abf287eb07a", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-27": { + "state": "c3791a827ab50ab793e374dc7897ff43339a54919c3912ae86386c9f8539aba0", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-28": { + "state": "8782ed1e33e4451f42f1d64b721e54e263b22e898a3ae2439fcf8cf7d1902031", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-29": { + "state": "f228f30b9369ede3acb87821a1db4b2587045e862639f2faac887268bd10f168", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-30": { + "state": "feee9e69fe161950f922095d4d4851657f9b12bfc79fb7de8e3bcc3fe17d8e5f", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-31": { + "state": "471331f9d3008258bbf3cab9cbd5af72690f6f34786b0052ccd9c6bac2828ebb", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + } + } + } +] diff --git a/benchmarks/v1/evidence/installed-extensions-040/README.md b/benchmarks/v1/evidence/installed-extensions-040/README.md new file mode 100644 index 0000000..25fd765 --- /dev/null +++ b/benchmarks/v1/evidence/installed-extensions-040/README.md @@ -0,0 +1,19 @@ +# Installed D2/D3 development evidence + +Produced at parent 2f6c77a plus the recorded dirty extension/verifier sources. +No src/openboost changes were made; the core wheel matches Sprint 039's hash. +This is repository-authored exploratory CPU evidence, not E5, external adoption, +complete E2/E6, real-data quality or GPU performance. + +- manifest.json: environment, versions, exact commands and working directories, + source/wheel hashes, artifact hashes and installation/inference status. +- checks.json: deterministic fixture, three-round raw outputs, constrained cuts + and maximum leaf-oracle difference. +- d2.json / d3.json: core inference artifacts created with the two plugins. + +The isolated environment was removed after verification. Its absolute paths in +the command log identify the actual run; the reproduction script creates fresh +paths. Run `uv run --no-sync python examples/v1_extensions/verify.py OUTPUT_DIR` +from the repository with offline build dependencies cached. Python 3.12 and +NumPy 2.3.5 match this record. Checks use one BLAS/OpenMP thread and fixed seed +123 for the solver fixtures. No time, memory or quality benchmark is claimed. diff --git a/benchmarks/v1/evidence/installed-extensions-040/checks.json b/benchmarks/v1/evidence/installed-extensions-040/checks.json new file mode 100644 index 0000000..2213353 --- /dev/null +++ b/benchmarks/v1/evidence/installed-extensions-040/checks.json @@ -0,0 +1,93 @@ +{ + "values": [ + [ + 0.0, + "a" + ], + [ + 1.0, + "b" + ], + [ + 2.0, + null + ], + [ + 3.0, + "a" + ], + [ + null, + "b" + ], + [ + 5.0, + "a" + ] + ], + "row_ids": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "names": [ + "x", + "category" + ], + "kinds": [ + "numeric", + "categorical" + ], + "predictions": { + "d2": [ + [ + 1.5801874999999999 + ], + [ + 1.5801874999999999 + ], + [ + 1.7251249999999998 + ], + [ + 2.106375 + ], + [ + 2.7743 + ], + [ + 2.7743 + ] + ], + "d3": [ + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.164000000000001 + ] + ] + }, + "d2_cuts": [ + 1, + 1, + 1 + ], + "d3_max_absolute_error": 8.881784197001252e-16, + "rounds": 3 +} diff --git a/benchmarks/v1/evidence/installed-extensions-040/d2.json b/benchmarks/v1/evidence/installed-extensions-040/d2.json new file mode 100644 index 0000000..b6d4143 --- /dev/null +++ b/benchmarks/v1/evidence/installed-extensions-040/d2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [2.25], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.0], [2.0]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [126, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.2874999999999996], [1.5249999999999997]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, 0, -1, -1, -1], "threshold": [190, 63, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.008472222222222017], [-1.7332499999999995], [1.718], [-2.410625], [-0.9612499999999998]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/installed-extensions-040/d3.json b/benchmarks/v1/evidence/installed-extensions-040/d3.json new file mode 100644 index 0000000..5efc3b8 --- /dev/null +++ b/benchmarks/v1/evidence/installed-extensions-040/d3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [4.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.76], [3.76]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/installed-extensions-040/manifest.json b/benchmarks/v1/evidence/installed-extensions-040/manifest.json new file mode 100644 index 0000000..e64ca8c --- /dev/null +++ b/benchmarks/v1/evidence/installed-extensions-040/manifest.json @@ -0,0 +1,138 @@ +{ + "schema": "openboost-v1-development-extensions-v1", + "passed": true, + "claim": "repository-authored D2/D3 installation and correctness only", + "commit": "2f6c77aa451dcacda8df627c84c06db14d187ace", + "dirty": true, + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "threads": 1, + "commands": [ + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/cohort_splits" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/penalized_leaves" + }, + { + "argv": [ + "uv", + "venv", + "--python", + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/env" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0" + }, + { + "argv": [ + "uv", + "pip", + "install", + "--offline", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/env/bin/python", + "numpy==2.3.5", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/wheels/ob_cohort_splits-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/wheels/ob_penalized_leaves-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/wheels/openboost-1.0.0.dev0-py3-none-any.whl" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/checks.py", + "/private/tmp/openboost-v1-extension-evidence-040-routed" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/env/bin/python", + "-I", + "-c", + "import json,importlib.metadata as m; print(json.dumps({n:m.version(n) for n in ['openboost','numpy','ob-cohort-splits','ob-penalized-leaves']}))" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0" + }, + { + "argv": [ + "uv", + "pip", + "uninstall", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/env/bin/python", + "ob-cohort-splits", + "ob-penalized-leaves" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0/core_inference.py", + "/private/tmp/openboost-v1-extension-evidence-040-routed" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-orkplye0" + } + ], + "sources": { + "examples/v1_extensions/checks.py": "47556dd3e9b5681d1bddb0ae8e086748fe273ddf39e25ab35b5e6a27d34e985b", + "examples/v1_extensions/cohort_splits/pyproject.toml": "d29f65c7783437a6220ec2dcfc6c4497792e804226c2b8f55d33f25560267245", + "examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py": "013730f77db77255d3be58ce16fe4883e55e4dcea80ac757a681fa5444e82178", + "examples/v1_extensions/core_inference.py": "e613307c01c51aba028cf1a26c1f06b61079c826a4a53f24fa57f8eaca083d6a", + "examples/v1_extensions/penalized_leaves/pyproject.toml": "e00f1e32176ca2f5295e233c2421c39f729babfea0d283228896b53fd914e4fe", + "examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py": "54208a6c52fe2acacc539f25428f31e28303a286c50dd5892b403c08e1fa0be2", + "examples/v1_extensions/verify.py": "7caf43d678373dc2df5253b3305cd0cfaa6f6af2c0539e5383b4bbd55fedb350" + }, + "wheels": { + "ob_cohort_splits-0.1.0-py3-none-any.whl": "2e9ebb56cd551f6ebeb150e866c5107f508d62069c45841ffdf0e51c6305141b", + "ob_penalized_leaves-0.1.0-py3-none-any.whl": "e7723fc496bcaf908b52f6ed11483833bbdb1f51da7c5e02eead10d27ed90236", + "openboost-1.0.0.dev0-py3-none-any.whl": "efc7705eb2cde914e4f1445cb087904bee02552df369b270728d7f80f4f7ec01" + }, + "versions": { + "openboost": "1.0.0.dev0", + "numpy": "2.3.5", + "ob-cohort-splits": "0.1.0", + "ob-penalized-leaves": "0.1.0" + }, + "plugin_free_inference": true, + "artifacts": { + "checks.json": "184006082086b9b5593ea7a7e18c9fdbd34ecae7beb52fa72ac64b883ffd045c", + "d2.json": "facc6da0ae50d74322f4552cf70a11c749f480a7dfb7d102579cec825e88e657", + "d3.json": "f701c628ebbf191056b72bb2a1d3b92d33385189423fa53bc609b33107f803f2" + } +} diff --git a/benchmarks/v1/evidence/lightgbm-cuda-build-initial.log b/benchmarks/v1/evidence/lightgbm-cuda-build-initial.log new file mode 100644 index 0000000..107376f --- /dev/null +++ b/benchmarks/v1/evidence/lightgbm-cuda-build-initial.log @@ -0,0 +1,191 @@ +✓ Initialized. View run at +[private run URL omitted] +Building image im-PQuOpAG7kBNgVTmPGD2a60 + +=> Step 0: FROM base + +=> Step 1: COPY . / +Saving image... +Image saved, took 607.59ms + +Built image im-PQuOpAG7kBNgVTmPGD2a60 in 2.31s + + +Building image im-qoqWKUS1zT6DOpVv94ai6g + +=> Step 0: FROM base + +=> Step 1: ENV OMP_NUM_THREADS=2 + +=> Step 2: ENV OPENBLAS_NUM_THREADS=2 +Saving image... +Image saved, took 648.10ms + +Built image im-qoqWKUS1zT6DOpVv94ai6g in 2.46s + + +Building image im-0Os9lQcHFaxlUYhxjQUFMJ + +=> Step 0: FROM base + +=> Step 1: RUN apt-get update +Get:1 http://archive.ubuntu.com/ubuntu jammy InRelease [270 kB] +Get:2 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64 InRelease [1578 B] +Get:3 http://security.ubuntu.com/ubuntu jammy-security InRelease [129 kB] +Get:4 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [128 kB] +Get:5 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64 Packages [2920 kB] +Get:6 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [127 kB] +Get:7 http://archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [17.5 MB] +Get:8 http://archive.ubuntu.com/ubuntu jammy/restricted amd64 Packages [164 kB] +Get:9 http://archive.ubuntu.com/ubuntu jammy/multiverse amd64 Packages [266 kB] +Get:10 http://archive.ubuntu.com/ubuntu jammy/main amd64 Packages [1792 kB] +Get:11 http://archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 Packages [92.7 kB] +Get:12 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [1620 kB] +Get:13 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [4623 kB] +Get:14 http://archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [8004 kB] +Get:15 http://archive.ubuntu.com/ubuntu jammy-backports/universe amd64 Packages [35.6 kB] +Get:16 http://archive.ubuntu.com/ubuntu jammy-backports/main amd64 Packages [82.8 kB] +Get:17 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [4287 kB] +Get:18 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [1316 kB] +Get:19 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 Packages [84.1 kB] +Get:20 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [7706 kB] +Fetched 51.1 MB in 2s (20.6 MB/s) +Reading package lists... + +=> Step 2: RUN apt-get install -y build-essential libboost-dev +Reading package lists... +Building dependency tree... +Reading state information... +build-essential is already the newest version (12.9ubuntu3). +build-essential set to manually installed. +Suggested packages: + libboost-doc libboost1.74-doc libboost-atomic1.74-dev + libboost-chrono1.74-dev libboost-container1.74-dev libboost-context1.74-dev + libboost-contract1.74-dev libboost-coroutine1.74-dev + libboost-date-time1.74-dev libboost-exception1.74-dev libboost-fiber1.74-dev + libboost-filesystem1.74-dev libboost-graph1.74-dev + libboost-graph-parallel1.74-dev libboost-iostreams1.74-dev + libboost-locale1.74-dev libboost-log1.74-dev libboost-math1.74-dev + libboost-mpi1.74-dev libboost-mpi-python1.74-dev libboost-numpy1.74-dev + libboost-program-options1.74-dev libboost-python1.74-dev + libboost-random1.74-dev libboost-regex1.74-dev + libboost-serialization1.74-dev libboost-stacktrace1.74-dev + libboost-system1.74-dev libboost-test1.74-dev libboost-thread1.74-dev + libboost-timer1.74-dev libboost-type-erasure1.74-dev libboost-wave1.74-dev + libboost1.74-tools-dev libmpfrc++-dev libntl-dev libboost-nowide1.74-dev +The following NEW packages will be installed: + libboost-dev libboost1.74-dev +0 upgraded, 2 newly installed, 0 to remove and 121 not upgraded. +Need to get 9612 kB of archives. +After this operation, 142 MB of additional disk space will be used. +Get:1 http://archive.ubuntu.com/ubuntu jammy/main amd64 libboost1.74-dev amd64 1.74.0-14ubuntu3 [9609 kB] +Get:2 http://archive.ubuntu.com/ubuntu jammy/main amd64 libboost-dev amd64 1.74.0.3ubuntu7 [3490 B] +debconf: delaying package configuration, since apt-utils is not installed +Fetched 9612 kB in 0s (25.4 MB/s) +Selecting previously unselected package libboost1.74-dev:amd64. +(Reading database ... +(Reading database ... 5% +(Reading database ... 10% +(Reading database ... 15% +(Reading database ... 20% +(Reading database ... 25% +(Reading database ... 30% +(Reading database ... 35%(Reading database ... 40%(Reading database ... 45%(Reading database ... 50%(Reading database ... 55%(Reading database ... 60%(Reading database ... 65%(Reading database ... 70%(Reading database ... 75%(Reading database ... 80%(Reading database ... 85%(Reading database ... 90%(Reading database ... 95%(Reading database ... 100%(Reading database ... 14806 files and directories currently installed.) +Preparing to unpack .../libboost1.74-dev_1.74.0-14ubuntu3_amd64.deb ... +Unpacking libboost1.74-dev:amd64 (1.74.0-14ubuntu3) ... +Selecting previously unselected package libboost-dev:amd64. +Preparing to unpack .../libboost-dev_1.74.0.3ubuntu7_amd64.deb ... +Unpacking libboost-dev:amd64 (1.74.0.3ubuntu7) ... +Setting up libboost1.74-dev:amd64 (1.74.0-14ubuntu3) ... +Setting up libboost-dev:amd64 (1.74.0.3ubuntu7) ... +Saving image... +Image saved, took 1.94s + +Built image im-0Os9lQcHFaxlUYhxjQUFMJ in 18.10s + + +Building image im-mTgFsahJL7lvSUo0Y5xIHZ + +=> Step 0: FROM base + +=> Step 1: COPY --from=ghcr.io/astral-sh/uv:0.12.1 /uv /.uv/uv +Getting image source signatures +Copying blob sha256:7445d66de2378275b55f712aedb693474de22c09b1a32c55e3d3bd46d2f4f4c9 +Copying blob sha256:788dd9aef29e3de18dc5b0e9d3d01eb448d8963d57ce5a0dd33149e2714272d5 +Copying config sha256:17c878e0974e1e0c450b21fa0fd3ad4bfeda10e1d7c599337f85f837d9113555 +Writing manifest to image destination +Unpacking OCI image + • unpacking rootfs ... + • ... done + • unpacked image rootfs: /tmp/task-data/ta-01M1VMFSXZ0KY68YN2Z357940B/registry-unpackeojx7N + +=> Step 2: COPY /.0_requirements-cuda.txt /.uv/0/requirements-cuda.txt + +=> Step 3: RUN /.uv/uv pip install --python $(command -v python) --compile-bytecode --require-hashes --reinstall-package lightgbm --no-binary lightgbm --config-settings=cmake.define.USE_CUDA=ON --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=75 --requirements /.uv/0/requirements-cuda.txt +Using Python 3.12.1 environment at: /usr/local +Resolved 49 packages in 283ms + Building lightgbm==4.7.0 + × Failed to build `lightgbm==4.7.0` + ├─▶ The build backend returned an error + ╰─▶ Call to `scikit_build_core.build.build_wheel` failed (exit status: 1) + + [stdout] + *** scikit-build-core 1.0.3 using CMake 4.4.3 (wheel) + *** Configuring CMake... + loading initial cache file /tmp/tmpn_hdfx7q/build/CMakeInit.txt + -- Configuring incomplete, errors occurred! + + [stderr] + 2026-09-06 15:13:28,183 - scikit_build_core - INFO - RUN: + /tmp/.tmpHAo02L/builds-v0/.tmpD69tzr/lib/python3.12/site-packages/cmake/data/bin/cmake + -E capabilities + 2026-09-06 15:13:28,203 - scikit_build_core - INFO - CMake version: + 4.4.3 + 2026-09-06 15:13:28,203 - scikit_build_core - INFO - Implementation: + cpython linux on x86_64 + 2026-09-06 15:13:28,206 - scikit_build_core - INFO - Build directory: + /tmp/tmpn_hdfx7q/build + 2026-09-06 15:13:28,225 - scikit_build_core - INFO - RUN: + /tmp/.tmpHAo02L/builds-v0/.tmpD69tzr/bin/ninja --version + 2026-09-06 15:13:28,228 - scikit_build_core - INFO - Ninja version: + 1.13.2 + 2026-09-06 15:13:28,229 - scikit_build_core - INFO - RUN: + /tmp/.tmpHAo02L/builds-v0/.tmpD69tzr/lib/python3.12/site-packages/cmake/data/bin/cmake + -S. -B/tmp/tmpn_hdfx7q/build -DCMAKE_BUILD_TYPE:STRING=Release + -C/tmp/tmpn_hdfx7q/build/CMakeInit.txt + -DCMAKE_INSTALL_PREFIX=/tmp/tmpn_hdfx7q/wheel/platlib + -DCMAKE_MAKE_PROGRAM=/tmp/.tmpHAo02L/builds-v0/.tmpD69tzr/bin/ninja + -D__BUILD_FOR_PYTHON=ON -DCMAKE_CUDA_ARCHITECTURES=75 -DUSE_CUDA=ON + CMake Error at + /tmp/.tmpHAo02L/builds-v0/.tmpD69tzr/lib/python3.12/site-packages/cmake/data/share/cmake-4.4/Modules/CMakeDetermineCCompiler.cmake:48 + (message): + Could not find the compiler specified in the environment variable CC: + + clang. + Call Stack (most recent call first): + CMakeLists.txt:41 (project) + + + CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage + CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage + + *** CMake configuration failed + + +hint: Build failures usually indicate a problem with the package or the build environmentTerminating task due to error: failed to run builder command "/.uv/uv pip install --python $(command -v python) --compile-bytecode --require-hashes --reinstall-package lightgbm --no-binary lightgbm --config-settings=cmake.define.USE_CUDA=ON --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=75 --requirements /.uv/0/requirements-cuda.txt": container exit status: 1 +Runner failed with exit code: -1 +Stopping app - uncaught exception raised locally: RemoteError('Image build for im-mTgFsahJL7lvSUo0Y5xIHZ failed. See build logs for more details.'). +[modal-client] 2026-09-06T08:13:29-0700 Loop attempt for _run_app..heartbeat failed +Traceback (most recent call last): + File "/Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/modal/_utils/async_utils.py", line 508, in loop_coro + await asyncio.wait_for(async_f(), timeout=timeout) + File "/Users/jiaruixu/.local/share/uv/python/cpython-3.12.12-macos-x86_64-none/lib/python3.12/asyncio/tasks.py", line 520, in wait_for + return await fut + ^^^^^^^^^ + File "/Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/modal/runner.py", line 56, in _heartbeat + await client.stub.AppHeartbeat(request, retry=Retry(attempt_timeout=HEARTBEAT_TIMEOUT)) +modal.exception.ConflictError: App state is APP_STATE_STOPPED +╭─ Error ──────────────────────────────────────────────────────────────────────╮ +│ Image build for im-mTgFsahJL7lvSUo0Y5xIHZ failed. See build logs for more │ +│ details. │ +╰──────────────────────────────────────────────────────────────────────────────╯ diff --git a/benchmarks/v1/evidence/lightgbm-cuda-native-abort.log b/benchmarks/v1/evidence/lightgbm-cuda-native-abort.log new file mode 100644 index 0000000..ea56a53 --- /dev/null +++ b/benchmarks/v1/evidence/lightgbm-cuda-native-abort.log @@ -0,0 +1,333 @@ +✓ Initialized. View run at +[private run URL omitted] +Building image im-yg45ADNcfMZygzjlF9xmqY + +=> Step 0: FROM base + +=> Step 1: ENV CC=gcc + +=> Step 2: ENV CXX=g++ + +=> Step 3: ENV CMAKE_BUILD_PARALLEL_LEVEL=2 +Saving image... +Image saved, took 547.46ms + +Built image im-yg45ADNcfMZygzjlF9xmqY in 2.90s + + +Building image im-DNfm8DoqUf9JAdcqWhZhcI + +=> Step 0: FROM base + +=> Step 1: COPY --from=ghcr.io/astral-sh/uv:0.12.1 /uv /.uv/uv +Getting image source signatures +Copying blob sha256:7445d66de2378275b55f712aedb693474de22c09b1a32c55e3d3bd46d2f4f4c9 +Copying blob sha256:788dd9aef29e3de18dc5b0e9d3d01eb448d8963d57ce5a0dd33149e2714272d5 +Copying config sha256:17c878e0974e1e0c450b21fa0fd3ad4bfeda10e1d7c599337f85f837d9113555 +Writing manifest to image destination +Unpacking OCI image + • unpacking rootfs ... + • ... done + • unpacked image rootfs: /tmp/task-data/ta-01M1VMHT0T97RZZP5MK9W0C48B/registry-unpackeDxc3R + +=> Step 2: COPY /.0_requirements-cuda.txt /.uv/0/requirements-cuda.txt + +=> Step 3: RUN /.uv/uv pip install --python $(command -v python) --compile-bytecode --require-hashes --reinstall-package lightgbm --no-binary lightgbm --config-settings=cmake.define.USE_CUDA=ON --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=75 --requirements /.uv/0/requirements-cuda.txt +Using Python 3.12.1 environment at: /usr/local +Resolved 49 packages in 1.05s + Building lightgbm==4.7.0 + Built lightgbm==4.7.0 +Prepared 3 packages in 2m 59s +Uninstalled 3 packages in 19ms +Installed 3 packages in 8ms +Bytecode compiled 41 files in 106ms + ~ lightgbm==4.7.0 + ~ treelite==3.9.1 + ~ treelite-runtime==3.9.1 +Saving image... +Image saved, took 2.13s + +Built image im-DNfm8DoqUf9JAdcqWhZhcI in 191.14s + + +✓ Created objects. +├── 🔨 Created mount +│ /Users/jiaruixu/work_space/openboost/benchmarks/v1/capability_smoke.py +└── 🔨 Created function preflight. + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-d5d08c28-91c0-3a59-6e20-29b04e0fcaf7: XID: NVRM: Xid (PCI:0000:e8:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-d5d08c28-91c0-3a59-6e20-29b04e0fcaf7: XID: NVRM: Xid (PCI:0000:e8:00): 13, Graphics Exception: ESR 0x504730=0xc02000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-d5d08c28-91c0-3a59-6e20-29b04e0fcaf7: XID: NVRM: Xid (PCI:0000:e8:00): 43, pid=131552, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-0849165a-8b6f-894c-d4f9-3ad1646d6524: XID: NVRM: Xid (PCI:0000:19:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-0849165a-8b6f-894c-d4f9-3ad1646d6524: XID: NVRM: Xid (PCI:0000:19:00): 13, Graphics Exception: ESR 0x504730=0xc03000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-0849165a-8b6f-894c-d4f9-3ad1646d6524: XID: NVRM: Xid (PCI:0000:19:00): 43, pid=15623, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-7a07efdf-c4a0-6364-8ae7-49ace2c246cd: XID: NVRM: Xid (PCI:0000:19:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-7a07efdf-c4a0-6364-8ae7-49ace2c246cd: XID: NVRM: Xid (PCI:0000:19:00): 13, Graphics Exception: ESR 0x504730=0xc01000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-7a07efdf-c4a0-6364-8ae7-49ace2c246cd: XID: NVRM: Xid (PCI:0000:19:00): 43, pid=488555, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-af1bf42c-0461-82b5-6f9f-00d062d15f85: XID: NVRM: Xid (PCI:0000:f5:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-af1bf42c-0461-82b5-6f9f-00d062d15f85: XID: NVRM: Xid (PCI:0000:f5:00): 13, Graphics Exception: ESR 0x504730=0xc02000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-af1bf42c-0461-82b5-6f9f-00d062d15f85: XID: NVRM: Xid (PCI:0000:f5:00): 43, pid=1883768, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-a8ea32a1-7ff4-a1a2-5625-97a05155663f: XID: NVRM: Xid (PCI:0000:e8:00): 13, pid=1714705, name=nvc:[driver], Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-a8ea32a1-7ff4-a1a2-5625-97a05155663f: XID: NVRM: Xid (PCI:0000:e8:00): 13, pid=1714705, name=nvc:[driver], Graphics Exception: ESR 0x504730=0xc01000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-a8ea32a1-7ff4-a1a2-5625-97a05155663f: XID: NVRM: Xid (PCI:0000:e8:00): 43, pid=1714060, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-ee14fea2-5b1f-c4c3-bb85-7b35f074b8db: XID: NVRM: Xid (PCI:0000:18:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-ee14fea2-5b1f-c4c3-bb85-7b35f074b8db: XID: NVRM: Xid (PCI:0000:18:00): 13, Graphics Exception: ESR 0x504730=0xc01000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-ee14fea2-5b1f-c4c3-bb85-7b35f074b8db: XID: NVRM: Xid (PCI:0000:18:00): 43, pid=786431, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-d7e75570-27cc-4a1a-6517-14da93c561f2: XID: NVRM: Xid (PCI:0000:19:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-d7e75570-27cc-4a1a-6517-14da93c561f2: XID: NVRM: Xid (PCI:0000:19:00): 13, Graphics Exception: ESR 0x504730=0xc02000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-d7e75570-27cc-4a1a-6517-14da93c561f2: XID: NVRM: Xid (PCI:0000:19:00): 43, pid=2017101, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 + +========== +== CUDA == +========== + +CUDA Version 12.4.0 + +Container image Copyright (c) 2016-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +This container image and its contents are governed by the NVIDIA Deep Learning Container License. +By pulling and using the container, you accept the terms and conditions of this license: +https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license + +A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience. + +[gpu-health] [WARN] GPU-ad9c7bc5-6aa7-63c3-625c-09e00b71525a: XID: NVRM: Xid (PCI:0000:18:00): 13, Graphics SM Warp Exception on (GPC 0, TPC 0, SM 0): Out Of Range Address + +[gpu-health] [WARN] GPU-ad9c7bc5-6aa7-63c3-625c-09e00b71525a: XID: NVRM: Xid (PCI:0000:18:00): 13, Graphics Exception: ESR 0x504730=0xc02000e 0x504734=0x20 0x504728=0x4c1eb72 0x50472c=0x174 + +[gpu-health] [WARN] GPU-ad9c7bc5-6aa7-63c3-625c-09e00b71525a: XID: NVRM: Xid (PCI:0000:18:00): 43, pid=2242932, name=exe, channel 0x0000000b + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/objective/cuda/cuda_regression_objective.cu 441 + +[LightGBM] [Fatal] [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +terminate called after throwing an instance of 'std::runtime_error' + what(): [CUDA] an illegal memory access was encountered /tmp/.tmpObN5zh/sdists-v9/index/c7ae78c95e4a8025/lightgbm/4.7.0/LdSDZoL666C8KeEm/src/src/io/cuda/cuda_tree.cpp 38 + +Runner aborted (SIGABRT), exit code: 134 +Stopping app - uncaught exception raised locally: InternalFailure('Server has lost track of input'). +╭───────────────────── Traceback (most recent call last) ──────────────────────╮ +│ /Users/jiaruixu/work_space/openboost/benchmarks/v1/modal_preflight.py:78 in │ +│ main │ +│ │ +│ 77 def main(): │ +│ ❱ 78 │ result = preflight.remote() │ +│ 79 │ result.update( │ +│ │ +│ /Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/moda │ +│ l/_object.py:340 in wrapped │ +│ │ +│ 339 │ │ await self.hydrate() │ +│ ❱ 340 │ │ return await method(self, *args, **kwargs) │ +│ 341 │ +│ │ +│ /Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/moda │ +│ l/_functions.py:1766 in remote │ +│ │ +│ 1765 │ │ │ +│ ❱ 1766 │ │ return await self._call_function(args, kwargs) │ +│ 1767 │ +│ │ +│ /Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/moda │ +│ l/_functions.py:1710 in _call_function │ +│ │ +│ 1709 │ │ │ +│ ❱ 1710 │ │ return await invocation.run_function() │ +│ 1711 │ +│ │ +│ /Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/moda │ +│ l/_functions.py:496 in run_function │ +│ │ +│ 495 │ │ │ # No more retries left. │ +│ ❱ 496 │ │ │ return await _process_result( │ +│ 497 │ │ │ │ await_response.output.result, await_response.output.d │ +│ │ +│ /Users/jiaruixu/work_space/openboost/.venv/lib/python3.12/site-packages/moda │ +│ l/_utils/function_utils.py:493 in _process_result │ +│ │ +│ 492 │ elif result.status == api_pb2.GenericResult.GENERIC_STATUS_INTERNA │ +│ ❱ 493 │ │ raise InternalFailure(result.exception) │ +│ 494 │ elif result.status != api_pb2.GenericResult.GENERIC_STATUS_SUCCESS │ +╰──────────────────────────────────────────────────────────────────────────────╯ +InternalFailure: Server has lost track of input diff --git a/benchmarks/v1/evidence/modal-capabilities-initial-source.txt b/benchmarks/v1/evidence/modal-capabilities-initial-source.txt new file mode 100644 index 0000000..13576b3 --- /dev/null +++ b/benchmarks/v1/evidence/modal-capabilities-initial-source.txt @@ -0,0 +1,271 @@ +"""Installed baseline capability probes: tiny weighted fit, prediction, and reload. + +These probes are not real-data quality results or performance measurements. +Run separately on CPU and a real CUDA host; failures remain in the returned record. +""" + +import argparse +import importlib.metadata +import json +import os +import platform +import subprocess +import tempfile +import time +from pathlib import Path + +import numpy as np + + +def run(device="cpu"): + import catboost as cb + import lightgbm as lgb + import xgboost as xgb + + rng = np.random.default_rng(41) + x = rng.normal(size=(96, 5)) + y = 2 + x[:, 0] + 0.1 * rng.normal(size=96) + weights = np.where(x[:, 1] > 0, 3.0, 0.5) + binary = (x[:, 0] > 0).astype(int) + multi = np.arange(96) % 3 + positive = np.exp(y / 2) + count = rng.poisson(positive) + rounds = 4 + cells = [] + for library in ["xgboost", "lightgbm", "catboost"]: + for task in ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11"]: + start = time.perf_counter() + record = {"library": library, "application": task, "device": device, "status": "error"} + try: + if ( + (library == "lightgbm" and task in ["A10", "A11"]) + or (library == "catboost" and task == "A8") + or (library == "xgboost" and task == "A11") + ): + record.update( + status="unsupported", + reason="no matching builtin; separate outer-loop control required", + ) + continue + target = { + "A2": binary, + "A3": multi, + "A4": multi, + "A6": np.column_stack([y, 2 * y]), + "A7": count, + "A8": positive, + "A9": positive, + "A10": positive, + }.get(task, y) + with tempfile.TemporaryDirectory() as temp: + model_path = Path(temp) / "model.json" + if library == "xgboost": + objectives = { + "A1": "reg:squarederror", + "A2": "binary:logistic", + "A3": "multi:softprob", + "A4": "rank:pairwise", + "A5": "reg:quantileerror", + "A6": "reg:squarederror", + "A7": "count:poisson", + "A8": "reg:gamma", + "A9": "reg:tweedie", + "A10": "survival:aft", + } + param = { + "objective": objectives[task], + "tree_method": "hist", + "device": device, + "max_depth": 2, + "eta": 0.1, + "nthread": 2, + "seed": 41, + } + d = xgb.DMatrix(x, label=target if task != "A10" else None) + if task == "A4": + d.set_group([8] * 12) + d.set_weight(np.linspace(0.5, 2, 12)) + else: + d.set_weight(weights) + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["quantile_alpha"] = 0.5 + if task == "A6": + param["multi_strategy"] = "multi_output_tree" + if task == "A9": + param["tweedie_variance_power"] = 1.5 + if task == "A10": + d.set_float_info("label_lower_bound", positive) + d.set_float_info( + "label_upper_bound", + np.where(np.arange(96) % 4 == 0, np.inf, positive), + ) + param.update( + aft_loss_distribution="normal", aft_loss_distribution_scale=1.0 + ) + model = xgb.train(param, d, num_boost_round=rounds) + # Actual build/config is recorded; CUDA host additionally validates GPU visibility. + actual = json.loads(model.save_config())["learner"]["generic_param"][ + "device" + ] + if device == "cuda" and not actual.startswith("cuda"): + raise ValueError("silent CPU fallback") + before = model.predict(d) + model.save_model(model_path) + loaded = xgb.Booster() + loaded.load_model(model_path) + after = loaded.predict(d) + record["effective_config"] = json.loads(model.save_config()) + elif library == "lightgbm": + objectives = { + "A1": "regression", + "A2": "binary", + "A3": "multiclass", + "A4": "lambdarank", + "A5": "quantile", + "A6": "regression", + "A7": "poisson", + "A8": "gamma", + "A9": "tweedie", + } + param = { + "objective": objectives[task], + "device_type": device, + "num_leaves": 4, + "learning_rate": 0.1, + "num_threads": 2, + "min_data_in_leaf": 2, + "verbosity": -1, + "seed": 41, + } + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["alpha"] = 0.5 + if task == "A9": + param["tweedie_variance_power"] = 1.5 + targets = target.T if task == "A6" else [target] + predictions = [] + restored = [] + for t in targets: + d = lgb.Dataset( + x, + label=t, + weight=weights + if task != "A4" + else np.repeat(np.linspace(0.5, 2, 12), 8), + group=[8] * 12 if task == "A4" else None, + ) + model = lgb.train(param, d, num_boost_round=rounds) + predictions.append(model.predict(x)) + model.save_model(str(model_path)) + restored.append(lgb.Booster(model_file=str(model_path)).predict(x)) + before = np.column_stack(predictions) if task == "A6" else predictions[0] + after = np.column_stack(restored) if task == "A6" else restored[0] + record["effective_config"] = param + else: + losses = { + "A1": "RMSE", + "A2": "Logloss", + "A3": "MultiClass", + "A4": "PairLogit", + "A5": "Quantile:alpha=0.5", + "A6": "MultiRMSE", + "A7": "Poisson", + "A9": "Tweedie:variance_power=1.5", + "A10": "SurvivalAft:dist=Normal;scale=1.0", + "A11": "RMSEWithUncertainty", + } + klass = ( + cb.CatBoostClassifier + if task in ["A2", "A3"] + else cb.CatBoostRanker + if task == "A4" + else cb.CatBoostRegressor + ) + model = klass( + loss_function=losses[task], + iterations=rounds, + depth=2, + learning_rate=0.1, + thread_count=2, + random_seed=41, + task_type="GPU" if device == "cuda" else "CPU", + verbose=False, + allow_writing_files=False, + ) + if task == "A10": + target = np.column_stack( + [positive, np.where(np.arange(96) % 4 == 0, -1, positive)] + ) + if task == "A4": + d = cb.Pool( + x, + target, + group_id=np.repeat(np.arange(12), 8), + group_weight=np.repeat(np.linspace(0.5, 2, 12), 8), + ) + else: + d = cb.Pool(x, target, weight=weights) + model.fit(d) + before = ( + model.predict(d) + if task == "A4" + else model.predict(d, prediction_type="RawFormulaVal") + ) + model.save_model(str(model_path)) + loaded = klass() + loaded.load_model(str(model_path)) + after = ( + loaded.predict(d) + if task == "A4" + else loaded.predict(d, prediction_type="RawFormulaVal") + ) + record["effective_config"] = model.get_all_params() + if not np.isfinite(before).all() or len(before) != 96: + raise ValueError("invalid predictions") + np.testing.assert_allclose(before, after, rtol=1e-7, atol=1e-8) + record.update( + status="pass", + prediction_shape=list(np.shape(before)), + reload_max_abs_error=float(np.max(np.abs(before - after))), + ) + except Exception as exc: + record.update(status="error", reason=f"{type(exc).__name__}: {exc}") + finally: + record["diagnostic_wall_s"] = time.perf_counter() - start + cells.append(record) + return { + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates", + "device": device, + "environment": { + "python": platform.python_version(), + "os": platform.platform(), + "cpu": platform.processor(), + "cpu_count": os.cpu_count(), + "packages": {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + }, + "cells": cells, + } + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--device", choices=["cpu", "cuda"], default="cpu") + p.add_argument("--output", type=Path, required=True) + a = p.parse_args() + result = run(a.device) + result["source_sha"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + result["dirty"] = bool(subprocess.check_output(["git", "status", "--porcelain"])) + result["source_file_sha256"] = ( + __import__("hashlib").sha256(Path(__file__).read_bytes()).hexdigest() + ) + a.output.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + print([(r["library"], r["application"], r["status"]) for r in result["cells"]]) + return int(any(r["status"] == "error" for r in result["cells"])) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/v1/evidence/modal-capabilities-initial.json b/benchmarks/v1/evidence/modal-capabilities-initial.json new file mode 100644 index 0000000..6348709 --- /dev/null +++ b/benchmarks/v1/evidence/modal-capabilities-initial.json @@ -0,0 +1,3904 @@ +{ + "cpu": { + "cells": [ + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.3830224169999994, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.005656944000000053, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5.8171743E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "binary:logistic" + }, + "metrics": [ + { + "name": "logloss" + } + ], + "objective": { + "name": "binary:logistic", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.0434905600000004, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "12" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[1.3698882E-1,2.1029353E-2,-1.5801823E-1]", + "boost_from_average": "1", + "num_class": "3", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "multi:softprob" + }, + "metrics": [ + { + "name": "mlogloss" + } + ], + "objective": { + "name": "multi:softprob", + "softmax_multiclass_param": { + "num_class": "3" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.0069513910000003065, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[6.961444E-11]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "rank:pairwise" + }, + "metrics": [ + { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "ndcg@32" + } + ], + "objective": { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "rank:pairwise" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.005912761000000266, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.1168752E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:quantileerror" + }, + "metrics": [ + { + "name": "quantile", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + ], + "objective": { + "name": "reg:quantileerror", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.005949257999999347, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0,4.183422E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "2" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "multi_output_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.00588566000000057, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0.699999988", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.0581717E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "count:poisson" + }, + "metrics": [ + { + "name": "poisson-nloglik" + } + ], + "objective": { + "name": "count:poisson", + "poisson_regression_param": { + "max_delta_step": "0.699999988" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.007663702999999522, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:gamma" + }, + "metrics": [ + { + "name": "gamma-deviance" + } + ], + "objective": { + "name": "reg:gamma", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.006024886000000507, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:tweedie" + }, + "metrics": [ + { + "name": "tweedie-nloglik@1.5" + } + ], + "objective": { + "name": "reg:tweedie", + "tweedie_regression_param": { + "tweedie_variance_power": "1.5" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.005656416999999969, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "survival:aft" + }, + "metrics": [ + { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "aft-nloglik" + } + ], + "objective": { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "survival:aft" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 4.896000000087497e-06, + "library": "xgboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.029381476000000184, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.011850205000000003, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "binary", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.010226790000000818, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_class": 3, + "num_leaves": 4, + "num_threads": 2, + "objective": "multiclass", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.025762676999999456, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "lambdarank", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.010783120000000146, + "effective_config": { + "alpha": 0.5, + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "quantile", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.020677143000000342, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.012616690999999847, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "poisson", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.00849402099999974, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "gamma", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.191018315, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "tweedie", + "seed": 41, + "tweedie_variance_power": 1.5, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 5.48200000061172e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 1.0960000000892478e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.08246851699999969, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.008531944000000458, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "class_names": [ + 0, + 1 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Logloss", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Logloss", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.012008214999999822, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "class_names": [ + 0, + 1, + 2 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiClass", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiClass", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.007483903999999875, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "PairLogit", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "PairLogit", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.009821458000000227, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Quantile:alpha=0.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Exact", + "learning_rate": 0.10000000149011612, + "loss_function": "Quantile:alpha=0.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.011487091000000227, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiRMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiRMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.007893805000000143, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Poisson", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Poisson", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 4.138000000430964e-06, + "library": "catboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.010477585999999484, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Tweedie:variance_power=1.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Tweedie:variance_power=1.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.011838701000000285, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "SurvivalAft:dist=Normal;scale=1.0", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "SurvivalAft:dist=Normal;scale=1.0", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 0.013650984000000754, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSEWithUncertainty", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSEWithUncertainty", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + } + ], + "device": "cpu", + "environment": { + "cpu": "x86_64", + "cpu_count": 18, + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "packages": { + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7", + "aiosignal": "1.3.2", + "attrs": "25.3.0", + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cbor2": "5.7.0", + "certifi": "2025.4.26", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cuda-pathfinder": "1.8.1", + "cupy-cuda12x": "14.2.0", + "cycler": "0.12.1", + "flatbuffers": "25.12.19", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "frozenlist": "1.6.0", + "graphviz": "0.21", + "grpclib": "0.4.8", + "h2": "4.2.0", + "hpack": "4.1.0", + "hyperframe": "6.1.0", + "idna": "3.10", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "llvmlite": "0.49.0", + "matplotlib": "3.11.1", + "ml_dtypes": "0.6.0", + "mpmath": "1.3.0", + "multidict": "6.4.4", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numba": "0.67.0", + "numpy": "2.3.5", + "nvidia-nccl-cu13": "2.31.2", + "onnx": "1.22.0", + "onnxruntime": "1.29.0", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "pip": "23.3.2", + "plotly": "7.0.0", + "propcache": "0.3.1", + "protobuf": "6.31.1", + "py-boost": "0.5.2", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "setuptools": "69.0.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "treelite": "3.9.1", + "treelite-runtime": "3.9.1", + "typing_extensions": "4.13.2", + "ujson": "6.0.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2", + "yarl": "1.20.0" + }, + "python": "3.12.1" + }, + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates" + }, + "cuda": { + "cells": [ + { + "application": "A1", + "device": "cuda", + "diagnostic_wall_s": 0.18350402200000016, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "reason": "AssertionError: \nNot equal to tolerance rtol=1e-07, atol=1e-08\n\nMismatched elements: 4 / 96 (4.17%)\nMax absolute difference among violations: 2.3841858e-07\nMax relative difference among violations: 1.0590257e-07\n ACTUAL: array([1.601711, 2.344037, 1.702211, 2.691001, 2.112375, 2.344037,\n 2.112375, 2.473294, 2.112375, 2.112375, 1.885614, 1.601711,\n 2.181587, 2.344037, 1.971243, 2.473294, 1.702211, 1.601711,...\n DESIRED: array([1.601711, 2.344037, 1.702211, 2.691001, 2.112375, 2.344037,\n 2.112375, 2.473294, 2.112375, 2.112375, 1.885614, 1.601711,\n 2.181587, 2.344037, 1.971243, 2.473294, 1.702211, 1.601711,...", + "status": "error" + }, + { + "application": "A2", + "device": "cuda", + "diagnostic_wall_s": 0.02990288899999971, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[5.8171743E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "binary:logistic" + }, + "metrics": [ + { + "name": "logloss" + } + ], + "objective": { + "name": "binary:logistic", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cuda", + "diagnostic_wall_s": 0.05250861699999998, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "12" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[1.3698888E-1,2.1029353E-2,-1.5801823E-1]", + "boost_from_average": "1", + "num_class": "3", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "multi:softprob" + }, + "metrics": [ + { + "name": "mlogloss" + } + ], + "objective": { + "name": "multi:softprob", + "softmax_multiclass_param": { + "num_class": "3" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "reason": "AssertionError: \nNot equal to tolerance rtol=1e-07, atol=1e-08\n\nMismatched elements: 10 / 288 (3.47%)\nMax absolute difference among violations: 5.9604645e-08\nMax relative difference among violations: 1.5465737e-07\n ACTUAL: array([[0.375513, 0.294073, 0.330414],\n [0.400895, 0.366829, 0.232276],\n [0.327521, 0.297006, 0.375473],...\n DESIRED: array([[0.375513, 0.294073, 0.330414],\n [0.400895, 0.366829, 0.232276],\n [0.327521, 0.297006, 0.375473],...", + "status": "error" + }, + { + "application": "A4", + "device": "cuda", + "diagnostic_wall_s": 0.12095322399999997, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[1.1834456E-9]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "rank:pairwise" + }, + "metrics": [ + { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "ndcg@32" + } + ], + "objective": { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "rank:pairwise" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 7.450580596923828e-09, + "status": "pass" + }, + { + "application": "A5", + "device": "cuda", + "diagnostic_wall_s": 0.03561765700000041, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[2.1168752E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:quantileerror" + }, + "metrics": [ + { + "name": "quantile", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + ], + "objective": { + "name": "reg:quantileerror", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 1.1920928955078125e-07, + "status": "pass" + }, + { + "application": "A6", + "device": "cuda", + "diagnostic_wall_s": 0.040657702999999934, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0,4.183422E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "2" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "multi_output_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cuda", + "diagnostic_wall_s": 0.05076412000000019, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0.699999988", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[3.0581717E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "count:poisson" + }, + "metrics": [ + { + "name": "poisson-nloglik" + } + ], + "objective": { + "name": "count:poisson", + "poisson_regression_param": { + "max_delta_step": "0.699999988" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "reason": "AssertionError: \nNot equal to tolerance rtol=1e-07, atol=1e-08\n\nMismatched elements: 3 / 96 (3.12%)\nMax absolute difference among violations: 9.536743e-07\nMax relative difference among violations: 2.3568703e-07\n ACTUAL: array([2.82205 , 3.241399, 2.82205 , 4.046358, 3.117693, 3.241399,\n 2.82205 , 3.241399, 2.82205 , 2.82205 , 2.82205 , 2.82205 ,\n 3.117693, 3.241399, 2.82205 , 3.241399, 2.82205 , 2.82205 ,...\n DESIRED: array([2.822051, 3.241399, 2.822051, 4.046359, 3.117693, 3.241399,\n 2.822051, 3.241399, 2.822051, 2.822051, 2.822051, 2.822051,\n 3.117693, 3.241399, 2.822051, 3.241399, 2.822051, 2.822051,...", + "status": "error" + }, + { + "application": "A8", + "device": "cuda", + "diagnostic_wall_s": 0.039139667999999794, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:gamma" + }, + "metrics": [ + { + "name": "gamma-deviance" + } + ], + "objective": { + "name": "reg:gamma", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "reason": "AssertionError: \nNot equal to tolerance rtol=1e-07, atol=1e-08\n\nMismatched elements: 24 / 96 (25%)\nMax absolute difference among violations: 4.7683716e-07\nMax relative difference among violations: 1.4934578e-07\n ACTUAL: array([2.193323, 3.590291, 2.402892, 3.855507, 3.089234, 3.438072,\n 3.089234, 3.725384, 3.089234, 3.089234, 2.707103, 2.193323,\n 3.192839, 3.438072, 2.841821, 3.725384, 2.402892, 2.193323,...\n DESIRED: array([2.193323, 3.590291, 2.402892, 3.855507, 3.089234, 3.438073,\n 3.089234, 3.725384, 3.089234, 3.089234, 2.707104, 2.193323,\n 3.19284 , 3.438073, 2.841821, 3.725384, 2.402892, 2.193323,...", + "status": "error" + }, + { + "application": "A9", + "device": "cuda", + "diagnostic_wall_s": 0.07707194099999981, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:tweedie" + }, + "metrics": [ + { + "name": "tweedie-nloglik@1.5" + } + ], + "objective": { + "name": "reg:tweedie", + "tweedie_regression_param": { + "tweedie_variance_power": "1.5" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "reason": "AssertionError: \nNot equal to tolerance rtol=1e-07, atol=1e-08\n\nMismatched elements: 8 / 96 (8.33%)\nMax absolute difference among violations: 7.1525574e-07\nMax relative difference among violations: 1.9447104e-07\n ACTUAL: array([2.515899, 3.493243, 2.515899, 4.379984, 3.023813, 3.493243,\n 3.023813, 3.677954, 3.023813, 3.023813, 2.76391 , 2.515899,\n 3.124306, 3.493243, 2.915009, 3.677954, 2.515899, 2.515899,...\n DESIRED: array([2.515899, 3.493243, 2.515899, 4.379984, 3.023813, 3.493243,\n 3.023813, 3.677955, 3.023813, 3.023813, 2.763909, 2.515899,\n 3.124307, 3.493243, 2.915009, 3.677955, 2.515899, 2.515899,...", + "status": "error" + }, + { + "application": "A10", + "device": "cuda", + "diagnostic_wall_s": 0.02826052999999984, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[5E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "survival:aft" + }, + "metrics": [ + { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "aft-nloglik" + } + ], + "objective": { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "survival:aft" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "reason": "AssertionError: \nNot equal to tolerance rtol=1e-07, atol=1e-08\n\nMismatched elements: 7 / 96 (7.29%)\nMax absolute difference among violations: 1.1920929e-07\nMax relative difference among violations: 1.1899831e-07\n ACTUAL: array([0.917692, 1.084647, 0.917692, 1.084647, 1.001773, 1.084647,\n 0.917692, 1.084647, 0.917692, 1.001773, 0.917692, 0.667145,\n 1.084647, 1.084647, 0.917692, 1.084647, 0.917692, 0.667145,...\n DESIRED: array([0.917692, 1.084647, 0.917692, 1.084647, 1.001773, 1.084647,\n 0.917692, 1.084647, 0.917692, 1.001773, 0.917692, 0.667145,\n 1.084647, 1.084647, 0.917692, 1.084647, 0.917692, 0.667145,...", + "status": "error" + }, + { + "application": "A11", + "device": "cuda", + "diagnostic_wall_s": 2.561999999706188e-06, + "library": "xgboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cuda", + "diagnostic_wall_s": 0.004823978999999312, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A2", + "device": "cuda", + "diagnostic_wall_s": 0.0015145790000001824, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A3", + "device": "cuda", + "diagnostic_wall_s": 0.001887583000000248, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A4", + "device": "cuda", + "diagnostic_wall_s": 0.016491323000000335, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A5", + "device": "cuda", + "diagnostic_wall_s": 0.0015816040000000697, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A6", + "device": "cuda", + "diagnostic_wall_s": 0.0012373189999994594, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A7", + "device": "cuda", + "diagnostic_wall_s": 0.0022170899999993665, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A8", + "device": "cuda", + "diagnostic_wall_s": 0.001421319999999504, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A9", + "device": "cuda", + "diagnostic_wall_s": 0.0012854930000001374, + "library": "lightgbm", + "reason": "LightGBMError: CUDA Tree Learner was not enabled in this build.\nPlease recompile with CMake option -DUSE_CUDA=1 (NVIDIA GPUs) or -DUSE_ROCM=1 (AMD GPUs)", + "status": "error" + }, + { + "application": "A10", + "device": "cuda", + "diagnostic_wall_s": 2.650999999964654e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A11", + "device": "cuda", + "diagnostic_wall_s": 7.349999995298617e-07, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A1", + "device": "cuda", + "diagnostic_wall_s": 0.17909186000000066, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "RMSE", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSE", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A2", + "device": "cuda", + "diagnostic_wall_s": 0.5207225649999998, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "class_names": [ + 0, + 1 + ], + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Logloss", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Logloss", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A3", + "device": "cuda", + "diagnostic_wall_s": 0.44483919299999997, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "class_names": [ + 0, + 1, + 2 + ], + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "MultiClass", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiClass", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A4", + "device": "cuda", + "diagnostic_wall_s": 0.4474383820000014, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "PairLogit", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "PairLogit", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A5", + "device": "cuda", + "diagnostic_wall_s": 0.4545041429999994, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Quantile:alpha=0.5", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Gradient", + "learning_rate": 0.10000000149011612, + "loss_function": "Quantile:alpha=0.5", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A6", + "device": "cuda", + "diagnostic_wall_s": 0.3967703140000012, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "MultiRMSE", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiRMSE", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A7", + "device": "cuda", + "diagnostic_wall_s": 0.447613693000001, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Poisson", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Poisson", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A8", + "device": "cuda", + "diagnostic_wall_s": 5.251999999345003e-06, + "library": "catboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported" + }, + { + "application": "A9", + "device": "cuda", + "diagnostic_wall_s": 0.4198302639999998, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Tweedie:variance_power=1.5", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Tweedie:variance_power=1.5", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "application": "A10", + "device": "cuda", + "diagnostic_wall_s": 0.3697868470000003, + "library": "catboost", + "reason": "CatBoostError: catboost/cuda/train_lib/train.cpp:262: Error: optimization scheme is not supported for GPU learning Loss=SurvivalAft;OptimizationScheme=SymmetricTree", + "status": "error" + }, + { + "application": "A11", + "device": "cuda", + "diagnostic_wall_s": 0.4194144780000002, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "RMSEWithUncertainty", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSEWithUncertainty", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass" + } + ], + "device": "cuda", + "environment": { + "cpu": "x86_64", + "cpu_count": 18, + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "packages": { + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7", + "aiosignal": "1.3.2", + "attrs": "25.3.0", + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cbor2": "5.7.0", + "certifi": "2025.4.26", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cuda-pathfinder": "1.8.1", + "cupy-cuda12x": "14.2.0", + "cycler": "0.12.1", + "flatbuffers": "25.12.19", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "frozenlist": "1.6.0", + "graphviz": "0.21", + "grpclib": "0.4.8", + "h2": "4.2.0", + "hpack": "4.1.0", + "hyperframe": "6.1.0", + "idna": "3.10", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "llvmlite": "0.49.0", + "matplotlib": "3.11.1", + "ml_dtypes": "0.6.0", + "mpmath": "1.3.0", + "multidict": "6.4.4", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numba": "0.67.0", + "numpy": "2.3.5", + "nvidia-nccl-cu13": "2.31.2", + "onnx": "1.22.0", + "onnxruntime": "1.29.0", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "pip": "23.3.2", + "plotly": "7.0.0", + "propcache": "0.3.1", + "protobuf": "6.31.1", + "py-boost": "0.5.2", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "setuptools": "69.0.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "treelite": "3.9.1", + "treelite-runtime": "3.9.1", + "typing_extensions": "4.13.2", + "ujson": "6.0.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2", + "yarl": "1.20.0" + }, + "python": "3.12.1" + }, + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates" + }, + "cuda_driver": 13000, + "cuda_runtime": 12090, + "dirty": true, + "gpu_inventory": "Tesla T4, 580.95.05, 15360 MiB\n", + "lock_sha256": "8a34f52cbd747faf6c496ce7981f23dc3652c36b115c935f8128265662289801", + "scope": "comparator capability preflight only", + "source_file_sha256": "b6232a6daae49fc0f8e48983b277f52932c15b409e4c6e2f2bfee0918020b146", + "source_sha": "861d31d332c20c2d9edb5cfe39872cf32b7aff7b" +} diff --git a/benchmarks/v1/evidence/modal-capabilities-isolated.json b/benchmarks/v1/evidence/modal-capabilities-isolated.json new file mode 100644 index 0000000..b42c310 --- /dev/null +++ b/benchmarks/v1/evidence/modal-capabilities-isolated.json @@ -0,0 +1,4201 @@ +{ + "cpu": { + "cells": [ + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.32860586900000044, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.6352709819999998, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5.8171743E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "binary:logistic" + }, + "metrics": [ + { + "name": "logloss" + } + ], + "objective": { + "name": "binary:logistic", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.592351656, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "12" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[1.3698882E-1,2.1029353E-2,-1.5801823E-1]", + "boost_from_average": "1", + "num_class": "3", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "multi:softprob" + }, + "metrics": [ + { + "name": "mlogloss" + } + ], + "objective": { + "name": "multi:softprob", + "softmax_multiclass_param": { + "num_class": "3" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.6119235530000005, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[6.961444E-11]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "rank:pairwise" + }, + "metrics": [ + { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "ndcg@32" + } + ], + "objective": { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "rank:pairwise" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.27296897699999967, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.1168752E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:quantileerror" + }, + "metrics": [ + { + "name": "quantile", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + ], + "objective": { + "name": "reg:quantileerror", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.4528941280000005, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0,4.183422E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "2" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "multi_output_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.45542494000000033, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0.699999988", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.0581717E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "count:poisson" + }, + "metrics": [ + { + "name": "poisson-nloglik" + } + ], + "objective": { + "name": "count:poisson", + "poisson_regression_param": { + "max_delta_step": "0.699999988" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.5602513709999997, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:gamma" + }, + "metrics": [ + { + "name": "gamma-deviance" + } + ], + "objective": { + "name": "reg:gamma", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.4186746509999999, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:tweedie" + }, + "metrics": [ + { + "name": "tweedie-nloglik@1.5" + } + ], + "objective": { + "name": "reg:tweedie", + "tweedie_regression_param": { + "tweedie_variance_power": "1.5" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.42004898200000085, + "effective_config": { + "learner": { + "generic_param": { + "device": "cpu", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_quantile_histmaker", + "updater_seq": "grow_quantile_histmaker" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_quantile_histmaker" + } + ] + }, + "learner_model_param": { + "base_score": "[5E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "survival:aft" + }, + "metrics": [ + { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "aft-nloglik" + } + ], + "objective": { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "survival:aft" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cpu", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 3.612000000430271e-06, + "library": "xgboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.028155738000002373, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.021715528000001427, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "binary", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.025002227999998183, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_class": 3, + "num_leaves": 4, + "num_threads": 2, + "objective": "multiclass", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.034311838000000705, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "lambdarank", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.024214520999997546, + "effective_config": { + "alpha": 0.5, + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "quantile", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.03347102300000415, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.024263511999997434, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "poisson", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 0.025222687000002963, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "gamma", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.02638680600000498, + "effective_config": { + "device_type": "cpu", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "tweedie", + "seed": 41, + "tweedie_variance_power": 1.5, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 6.8080000019676845e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 3.2430000018734972e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A1", + "device": "cpu", + "diagnostic_wall_s": 0.07404617199999564, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A2", + "device": "cpu", + "diagnostic_wall_s": 0.06909995200000196, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "class_names": [ + 0, + 1 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Logloss", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Logloss", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A3", + "device": "cpu", + "diagnostic_wall_s": 0.07280764699999764, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "class_names": [ + 0, + 1, + 2 + ], + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiClass", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiClass", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A4", + "device": "cpu", + "diagnostic_wall_s": 0.06819500400000322, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "PairLogit", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "PairLogit", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A5", + "device": "cpu", + "diagnostic_wall_s": 0.06942154700000458, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Quantile:alpha=0.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Exact", + "learning_rate": 0.10000000149011612, + "loss_function": "Quantile:alpha=0.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A6", + "device": "cpu", + "diagnostic_wall_s": 0.07006206399999826, + "effective_config": { + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "MultiRMSE", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiRMSE", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A7", + "device": "cpu", + "diagnostic_wall_s": 0.06814975900000775, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Poisson", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Poisson", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A8", + "device": "cpu", + "diagnostic_wall_s": 3.6800000060566163e-06, + "library": "catboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A9", + "device": "cpu", + "diagnostic_wall_s": 0.06852739100000349, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "Tweedie:variance_power=1.5", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Tweedie:variance_power=1.5", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A10", + "device": "cpu", + "diagnostic_wall_s": 0.07189218500001004, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "SurvivalAft:dist=Normal;scale=1.0", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "SurvivalAft:dist=Normal;scale=1.0", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A11", + "device": "cpu", + "diagnostic_wall_s": 0.07012782900000047, + "effective_config": { + "auto_class_weights": "None", + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "MVS", + "border_count": 254, + "classes_count": 0, + "depth": 2, + "eval_fraction": 0, + "eval_metric": "RMSEWithUncertainty", + "feature_border_type": "GreedyLogSum", + "force_unit_auto_pair_weights": false, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSEWithUncertainty", + "max_leaves": 4, + "min_data_in_leaf": 1, + "model_shrink_mode": "Constant", + "model_shrink_rate": 0, + "model_size_reg": 0.5, + "nan_mode": "Min", + "penalties_coefficient": 1, + "pool_metainfo_options": { + "tags": {} + }, + "posterior_sampling": false, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "sampling_frequency": "PerTree", + "score_function": "Cosine", + "sparse_features_conflict_fraction": 0, + "subsample": 1, + "task_type": "CPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + } + ], + "environment": { + "cpu": "x86_64", + "cpu_count": 18, + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "packages": { + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7", + "aiosignal": "1.3.2", + "attrs": "25.3.0", + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cbor2": "5.7.0", + "certifi": "2025.4.26", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cuda-pathfinder": "1.8.1", + "cupy-cuda12x": "14.2.0", + "cycler": "0.12.1", + "flatbuffers": "25.12.19", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "frozenlist": "1.6.0", + "graphviz": "0.21", + "grpclib": "0.4.8", + "h2": "4.2.0", + "hpack": "4.1.0", + "hyperframe": "6.1.0", + "idna": "3.10", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "llvmlite": "0.49.0", + "matplotlib": "3.11.1", + "ml_dtypes": "0.6.0", + "mpmath": "1.3.0", + "multidict": "6.4.4", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numba": "0.67.0", + "numpy": "2.3.5", + "nvidia-nccl-cu13": "2.31.2", + "onnx": "1.22.0", + "onnxruntime": "1.29.0", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "pip": "23.3.2", + "plotly": "7.0.0", + "propcache": "0.3.1", + "protobuf": "6.31.1", + "py-boost": "0.5.2", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "setuptools": "69.0.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "treelite": "3.9.1", + "treelite-runtime": "3.9.1", + "typing_extensions": "4.13.2", + "ujson": "6.0.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2", + "yarl": "1.20.0" + }, + "python": "3.12.1" + } + }, + "cuda": { + "cells": [ + { + "application": "A1", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.539520882000005, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A2", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.7471460820000004, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[5.8171743E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "binary:logistic" + }, + "metrics": [ + { + "name": "logloss" + } + ], + "objective": { + "name": "binary:logistic", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A3", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.7389042490000008, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "12" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[1.3698888E-1,2.1029353E-2,-1.5801823E-1]", + "boost_from_average": "1", + "num_class": "3", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "multi:softprob" + }, + "metrics": [ + { + "name": "mlogloss" + } + ], + "objective": { + "name": "multi:softprob", + "softmax_multiclass_param": { + "num_class": "3" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A4", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.5842076469999995, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[1.1834456E-9]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "rank:pairwise" + }, + "metrics": [ + { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "ndcg@32" + } + ], + "objective": { + "lambdarank_param": { + "lambdarank_bias_norm": "1", + "lambdarank_normalization": "1", + "lambdarank_num_pair_per_sample": "4294967295", + "lambdarank_pair_method": "topk", + "lambdarank_score_normalization": "1", + "lambdarank_unbiased": "0", + "ndcg_exp_gain": "1" + }, + "name": "rank:pairwise" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A5", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.6612446159999905, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[2.1168752E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:quantileerror" + }, + "metrics": [ + { + "name": "quantile", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + ], + "objective": { + "name": "reg:quantileerror", + "quantile_loss_param": { + "quantile_alpha": "[5E-1]" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A6", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.8241807729999948, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[2.091711E0,4.183422E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "2" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "multi_output_tree", + "objective": "reg:squarederror" + }, + "metrics": [ + { + "name": "rmse" + } + ], + "objective": { + "name": "reg:squarederror", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A7", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.6829233090000031, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0.699999988", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[3.0581717E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "count:poisson" + }, + "metrics": [ + { + "name": "poisson-nloglik" + } + ], + "objective": { + "name": "count:poisson", + "poisson_regression_param": { + "max_delta_step": "0.699999988" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A8", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.6860423739999959, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:gamma" + }, + "metrics": [ + { + "name": "gamma-deviance" + } + ], + "objective": { + "name": "reg:gamma", + "reg_loss_param": { + "scale_pos_weight": "1" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A9", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.6371237119999904, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[3.1823819E0]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "reg:tweedie" + }, + "metrics": [ + { + "name": "tweedie-nloglik@1.5" + } + ], + "objective": { + "name": "reg:tweedie", + "tweedie_regression_param": { + "tweedie_variance_power": "1.5" + } + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A10", + "cpu_inference_max_abs_error": 0.0, + "device": "cuda", + "diagnostic_wall_s": 0.7891384429999988, + "effective_config": { + "learner": { + "generic_param": { + "device": "cuda:0", + "fail_on_invalid_gpu_id": "0", + "n_jobs": "2", + "nthread": "2", + "random_state": "41", + "rng_state": "41 1295319342 2949947021 3517322606 704170245 1306393598 4234732193 1092099057 2567337400 2225273963 3225470583 3773718479 4259575688 2469858532 3974704332 3466753210 3013479949 692753148 2281232766 334834175 3442215087 1337423857 706855366 2221828405 1784607179 2606792395 879400551 1230730686 2941365111 3962389062 1379209815 2652520973 2417956619 3973506990 3296722019 1798173187 1437699310 2181136752 3137082400 2435326865 3169104935 1940250562 3347424025 2847425389 1037173239 145290656 226199630 265323765 3541995993 2618504499 4217517447 3808346439 3704333320 929995660 1622493810 3112364694 440123548 1743419845 2131226254 917808550 3514118330 3143514682 2430398550 1712571747 2551992810 1213823177 443002666 1774949845 961319144 1796821965 694827970 2872206801 1344608135 2766339623 378737123 3721234906 3519775721 38356383 411275017 3367124956 1704626763 3389008259 3938836434 3482265288 852095339 3653993100 3155298753 1655452998 2137326683 1755636187 2866857308 3252628593 989039446 2936078155 1526843691 344053745 660679541 365609610 2766183124 3277036753 2963751230 1284102417 2801546166 857193067 403492767 428015908 1682421662 183011622 2267902570 2818558837 4027512161 3303424537 2934832818 2550246369 1423592449 1623981171 1504811886 2202647104 814861952 3599300855 2951793852 147092847 2724445253 2332837758 2994573416 1220064335 1602150724 3632254392 1403141703 239077407 812960701 3980887828 1243913111 1624156339 2125320384 546416044 3805505892 4137792812 2685117205 2290067358 2421398808 4089832911 1839322634 1521636838 94733235 3953967408 988906673 3458405224 3307728075 4293882749 1857617740 649588472 3728474224 1359919352 4137368535 1896812351 1019949586 2960417719 4248450055 703113779 927297983 2826835964 3297273304 3041878794 428432076 542900001 3854258603 4247920623 659547332 894313981 2685690491 3456932712 1846711523 3375345623 599053138 984367625 4018921277 2673426983 228582987 3798007626 4292987265 595137279 3806852689 4237562129 4077608658 3169927726 114075670 357308265 258698793 3799345130 1347203243 1165143505 1173545936 4218796854 2970958251 416952688 748319220 1301711369 1383775726 1067112210 1856863458 2960965976 2787603276 3110550673 288002507 777922788 4249102018 3756495860 2219675715 311669622 260699232 601148083 307414131 1760431412 2205127615 1463840616 1072309829 2914977554 3096795690 390331043 761798187 2573593300 3283889484 2124818442 2672341047 3949781706 651299631 3676541294 1943451109 3293104089 3533734888 3210334110 198010228 1233132461 3759011782 3246272164 2555430863 1164347854 1507665561 4228879079 4227660004 240309012 791608278 3978879585 4210410398 502280934 3343592372 1091448618 2953027567 3179212410 3589951058 2950774768 3333733750 2833603878 491914290 2868406969 876425159 2636536196 2159282400 3052827693 2947677839 72213414 2736301956 2802503397 3427680555 601234897 3055918463 143263836 1198688600 3374305834 1814355003 3791384049 2488886154 1983909305 4003783338 3807960768 381370115 699234372 1084807146 3918984142 396486137 1287716182 823690093 284962844 3389126696 2915233044 2724352204 3416924261 1346330462 1118009500 1314233363 2496280125 4203224831 3643610769 98306752 1557200615 1999087590 1188093516 1199172235 325280669 3419427613 3548240387 3226534702 1290032880 2503231301 3199606580 1460156288 2893606936 1290446198 2774595624 2771805128 1013086697 662096677 1318581202 36970105 2754463736 904573150 1548785363 1145037848 3874840604 605586555 3756988616 2670095449 3030545450 1096367372 2973502566 2517526714 1723976159 1821275102 1330581316 541062275 3869938682 1649124233 454249973 2164842487 604862968 1700714536 4114102910 736557475 2234366370 2529222004 2262140643 328442139 2251490302 2871422916 2925858935 3001097347 1050167616 839395228 4289601001 2535065776 3771380121 4231626018 2659977318 3900232918 258274924 1739130368 4025280714 2592762291 2600627516 2568833246 3801054261 1502537912 956027752 3881111924 2032768096 141980851 1880413966 2495144283 1392020110 3692966621 221569289 1971952385 1077749877 502467386 2826664281 1109759327 4082449807 1566913974 2930647470 2523682392 2808757503 1316517455 2432199749 233158787 3136612400 1145069372 3070769556 2016018354 1682812452 2212135711 3341500664 1689515919 3076369039 2652160043 103072440 196955172 879382465 753320371 77084718 929089974 4045143135 3499871710 2343468740 2098854834 2832734772 3267575524 2147643834 2346342192 2150378067 2286488719 2959370812 1680546322 1350202908 3226542095 981209691 1564668551 3369952895 3633695886 2976655940 2453022018 2920018917 797891273 4260422644 2311725339 4230246790 834926627 252748410 3252250062 383443086 845715380 4066324403 501416480 3583178833 1277025292 2196096980 1534287650 811812228 3455718090 1731591172 2622364849 3306299224 2993361825 4281728010 1147945545 505851941 109727063 1767183378 2798917695 3092330194 1846538706 2507756802 979077828 2126292249 2557410366 3194593651 3173592157 4103494212 1636998861 1567085639 3229235050 666767162 1186467248 1908941220 2239337705 4028007816 287773865 2333645184 359212062 1537298347 1609329128 2387770308 892456182 101680359 3364884477 3512155921 2578089462 2074242849 1047394686 1985601173 1335756356 1117153306 4291463049 976639317 1013786989 1614261990 4276265481 2120698329 1244445728 1161487342 3821000245 667708217 328650857 1860660314 4106712277 3390152797 844666118 3755803471 3221879534 4088649332 1962560999 1136317363 2514340144 1770661041 3522341736 2254890544 2066191796 967765860 467466352 1244788269 1302834522 2154437094 1458781428 2152278954 3604161354 2184052432 2302799070 3100127441 1658925125 2273865947 522692773 3160987682 3655755434 1601565624 2131110665 4155393077 2679886172 3921078821 2273491214 2330881997 3432048061 2957988873 4086500715 151581725 1140250247 1322932981 1669232732 1977238474 3421594673 2050575317 1694195904 1866168130 1008593549 2687369408 2916884650 703253609 333830287 444332430 2253944106 1806738157 139760962 2047686449 4228872216 1713231824 1425801375 269667969 2037744657 729394301 3082482815 4110703744 3419380959 2648613117 1744240077 853036975 3552909631 132649697 3234913787 2331593999 3589430617 2718564539 1499745335 3470064009 2656522158 4022959385 3525789568 660165614 128613670 3074865215 3978975059 1570653395 469994270 1756033819 1698747784 1201246804 1262041809 2847770201 1965668145 3469604923 983623780 3365221825 1290865112 709690860 2718082156 1007402935 1815909509 3741138535 1695097800 2269163938 1458878070 1970449994 496101615 4072787876 2686996029 2038405174 2504883983 1441535870 1999715961 3055804343 2295472585 4287232632 1708697577 2544724203 3471710033 1235752895 3526889052 1751614690 4247382775 2901559469 3041220213 820104798 1830337410 2880444444 1244894276 177565352 624", + "seed": "41", + "seed_per_iteration": "0", + "validate_parameters": "1" + }, + "gradient_booster": { + "dart_train_param": { + "normalize_type": "tree", + "one_drop": "0", + "rate_drop": "0", + "sample_type": "uniform", + "skip_drop": "0" + }, + "gbtree_model_param": { + "num_parallel_tree": "1", + "num_trees": "4" + }, + "gbtree_train_param": { + "process_type": "default", + "tree_method": "hist", + "updater": "grow_gpu_hist", + "updater_seq": "grow_gpu_hist" + }, + "name": "gbtree", + "specified_updater": false, + "tree_train_param": { + "alpha": "0", + "colsample_bylevel": "1", + "colsample_bynode": "1", + "colsample_bytree": "1", + "eta": "0.100000001", + "gamma": "0", + "grow_policy": "depthwise", + "interaction_constraints": "", + "lambda": "1", + "learning_rate": "0.100000001", + "max_bin": "256", + "max_cat_threshold": "64", + "max_cat_to_onehot": "4", + "max_delta_step": "0", + "max_depth": "2", + "max_leaves": "0", + "min_child_weight": "1", + "min_split_loss": "0", + "monotone_constraints": "()", + "refresh_leaf": "1", + "reg_alpha": "0", + "reg_lambda": "1", + "sampling_method": "uniform", + "sparse_threshold": "0.20000000000000001", + "subsample": "1" + }, + "updater": [ + { + "hist_train_param": { + "debug_synchronize": "0", + "max_cached_hist_node": "18446744073709551615" + }, + "name": "grow_gpu_hist" + } + ] + }, + "learner_model_param": { + "base_score": "[5E-1]", + "boost_from_average": "1", + "num_class": "0", + "num_feature": "5", + "num_target": "1" + }, + "learner_train_param": { + "booster": "gbtree", + "disable_default_eval_metric": "0", + "multi_strategy": "one_output_per_tree", + "objective": "survival:aft" + }, + "metrics": [ + { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "aft-nloglik" + } + ], + "objective": { + "aft_loss_param": { + "aft_loss_distribution": "normal", + "aft_loss_distribution_scale": "1" + }, + "name": "survival:aft" + } + }, + "version": [ + 3, + 4, + 1 + ] + }, + "library": "xgboost", + "prediction_shape": [ + 96 + ], + "reload_device": "cuda", + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A11", + "device": "cuda", + "diagnostic_wall_s": 3.496999994467842e-06, + "library": "xgboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A1", + "device": "cuda", + "diagnostic_wall_s": 0.5113802660000033, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A2", + "device": "cuda", + "diagnostic_wall_s": 0.39472900700000935, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "binary", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A3", + "device": "cuda", + "diagnostic_wall_s": 0.8237444340000053, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_class": 3, + "num_leaves": 4, + "num_threads": 2, + "objective": "multiclass", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A4", + "device": "cuda", + "diagnostic_wall_s": 0.46935936200000583, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "lambdarank", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A5", + "device": "cuda", + "diagnostic_wall_s": 0.5086486139999948, + "effective_config": { + "alpha": 0.5, + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "quantile", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A6", + "device": "cuda", + "diagnostic_wall_s": 0.728950220999991, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "regression", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A7", + "device": "cuda", + "diagnostic_wall_s": 1.503241040000006, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "poisson", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A8", + "device": "cuda", + "diagnostic_wall_s": 2.076574937999993, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "gamma", + "seed": 41, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A9", + "device": "cuda", + "diagnostic_wall_s": 0.7656092750000028, + "effective_config": { + "device_type": "cuda", + "learning_rate": 0.1, + "min_data_in_leaf": 2, + "num_leaves": 4, + "num_threads": 2, + "objective": "tweedie", + "seed": 41, + "tweedie_variance_power": 1.5, + "verbosity": -1 + }, + "library": "lightgbm", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A10", + "device": "cuda", + "diagnostic_wall_s": 3.8710000183073134e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A11", + "device": "cuda", + "diagnostic_wall_s": 4.6760000032008975e-06, + "library": "lightgbm", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A1", + "device": "cuda", + "diagnostic_wall_s": 3.2860520039999983, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "RMSE", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSE", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A2", + "device": "cuda", + "diagnostic_wall_s": 3.9793308600000046, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "class_names": [ + 0, + 1 + ], + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Logloss", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Logloss", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A3", + "device": "cuda", + "diagnostic_wall_s": 2.8059179159999985, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "class_names": [ + 0, + 1, + 2 + ], + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "MultiClass", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiClass", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 3 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A4", + "device": "cuda", + "diagnostic_wall_s": 3.4601813849999985, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "PairLogit", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "PairLogit", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A5", + "device": "cuda", + "diagnostic_wall_s": 5.751963517999997, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Quantile:alpha=0.5", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Gradient", + "learning_rate": 0.10000000149011612, + "loss_function": "Quantile:alpha=0.5", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A6", + "device": "cuda", + "diagnostic_wall_s": 2.7399294120000093, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": true, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "MultiRMSE", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "MultiRMSE", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A7", + "device": "cuda", + "diagnostic_wall_s": 3.110428135999996, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Poisson", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Poisson", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A8", + "device": "cuda", + "diagnostic_wall_s": 3.8399999766625115e-06, + "library": "catboost", + "reason": "no matching builtin; separate outer-loop control required", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A9", + "device": "cuda", + "diagnostic_wall_s": 1.590638279000018, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "Tweedie:variance_power=1.5", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "Tweedie:variance_power=1.5", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A10", + "device": "cuda", + "diagnostic_wall_s": 1.1254999975562896e-05, + "library": "catboost", + "reason": "SurvivalAft GPU fit rejected by version 1.2.10; retained initial evidence", + "status": "unsupported", + "worker_exit_code": 0, + "worker_log": "" + }, + { + "application": "A11", + "device": "cuda", + "diagnostic_wall_s": 0.8828775769999879, + "effective_config": { + "add_ridge_penalty_to_loss_function": false, + "auto_class_weights": "None", + "bagging_temperature": 1, + "bayesian_matrix_reg": 0.10000000149011612, + "best_model_min_trees": 1, + "boost_from_average": false, + "boosting_type": "Plain", + "bootstrap_type": "Bayesian", + "border_count": 128, + "classes_count": 0, + "data_partition": "DocParallel", + "depth": 2, + "devices": "-1", + "eval_fraction": 0, + "eval_metric": "RMSEWithUncertainty", + "feature_border_type": "GreedyLogSum", + "fold_size_loss_normalization": false, + "force_unit_auto_pair_weights": false, + "gpu_cat_features_storage": "GpuRam", + "gpu_ram_part": 0.95, + "grow_policy": "SymmetricTree", + "iterations": 4, + "l2_leaf_reg": 3, + "leaf_estimation_backtracking": "AnyImprovement", + "leaf_estimation_iterations": 1, + "leaf_estimation_method": "Newton", + "learning_rate": 0.10000000149011612, + "loss_function": "RMSEWithUncertainty", + "max_leaves": 4, + "meta_l2_exponent": 1, + "meta_l2_frequency": 0, + "min_data_in_leaf": 1, + "min_fold_size": 100, + "model_size_reg": 0.5, + "nan_mode": "Min", + "observations_to_bootstrap": "TestOnly", + "penalties_coefficient": 1, + "pinned_memory_bytes": "104857600", + "pool_metainfo_options": { + "tags": {} + }, + "random_score_type": "NormalWithModelSizeDecrease", + "random_seed": 41, + "random_strength": 1, + "rsm": 1, + "score_function": "Cosine", + "task_type": "GPU", + "use_best_model": false + }, + "library": "catboost", + "prediction_shape": [ + 96, + 2 + ], + "reload_max_abs_error": 0.0, + "status": "pass", + "worker_exit_code": 0, + "worker_log": "" + } + ], + "environment": { + "cpu": "x86_64", + "cpu_count": 18, + "os": "Linux-4.19.0-gvisor-x86_64-with-glibc2.35", + "packages": { + "aiohappyeyeballs": "2.6.1", + "aiohttp": "3.12.7", + "aiosignal": "1.3.2", + "attrs": "25.3.0", + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cbor2": "5.7.0", + "certifi": "2025.4.26", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cuda-pathfinder": "1.8.1", + "cupy-cuda12x": "14.2.0", + "cycler": "0.12.1", + "flatbuffers": "25.12.19", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "frozenlist": "1.6.0", + "graphviz": "0.21", + "grpclib": "0.4.8", + "h2": "4.2.0", + "hpack": "4.1.0", + "hyperframe": "6.1.0", + "idna": "3.10", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "llvmlite": "0.49.0", + "matplotlib": "3.11.1", + "ml_dtypes": "0.6.0", + "mpmath": "1.3.0", + "multidict": "6.4.4", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numba": "0.67.0", + "numpy": "2.3.5", + "nvidia-nccl-cu13": "2.31.2", + "onnx": "1.22.0", + "onnxruntime": "1.29.0", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "pip": "23.3.2", + "plotly": "7.0.0", + "propcache": "0.3.1", + "protobuf": "6.31.1", + "py-boost": "0.5.2", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "setuptools": "69.0.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "treelite": "3.9.1", + "treelite-runtime": "3.9.1", + "typing_extensions": "4.13.2", + "ujson": "6.0.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2", + "yarl": "1.20.0" + }, + "python": "3.12.1" + } + }, + "cuda_driver": 13000, + "cuda_runtime": 12090, + "dirty": true, + "gpu_inventory": "Tesla T4, 580.95.05, 15360 MiB\n", + "lock_sha256": "8a34f52cbd747faf6c496ce7981f23dc3652c36b115c935f8128265662289801", + "scope": "comparator capability preflight only", + "source_file_sha256": "bd8b7a65e232a8d9d5d429fefd08be92ca6bc06c5fc65210198e0743a8031bb7", + "source_sha": "861d31d332c20c2d9edb5cfe39872cf32b7aff7b" +} diff --git a/benchmarks/v1/evidence/modal-native-abort-harness-source.txt b/benchmarks/v1/evidence/modal-native-abort-harness-source.txt new file mode 100644 index 0000000..4fe7e71 --- /dev/null +++ b/benchmarks/v1/evidence/modal-native-abort-harness-source.txt @@ -0,0 +1,93 @@ +"""Allowlisted comparator preflight; uploads no production source or datasets.""" + +import hashlib +import importlib.util +import json +import subprocess +from pathlib import Path + +import modal + +ROOT = Path(__file__).resolve().parents[2] +SOURCE = Path(__file__).with_name("capability_smoke.py") +LOCK = Path(__file__).with_name("requirements-cuda.txt") +SOURCE_SHA = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip() +DIRTY = bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=ROOT)) +HASH = hashlib.sha256(SOURCE.read_bytes()).hexdigest() +LOCK_HASH = hashlib.sha256(LOCK.read_bytes()).hexdigest() +app = modal.App("openboost-v1-comparator-preflight") +image = ( + modal.Image.from_registry( + "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + add_python="3.12", + ) + .uv_pip_install(requirements=[str(LOCK)], extra_options="--require-hashes", uv_version="0.12.1") + .add_local_file(SOURCE, "/opt/capability_smoke.py", copy=True) + .env({"OMP_NUM_THREADS": "2", "OPENBLAS_NUM_THREADS": "2"}) +) + + +# The standard wheel lacks CUDA: rebuild the same hash-locked release explicitly. +image = ( + image.apt_install("build-essential", "libboost-dev") + .env({"CC": "gcc", "CXX": "g++", "CMAKE_BUILD_PARALLEL_LEVEL": "2"}) + .uv_pip_install( + requirements=[str(LOCK)], + extra_options="--require-hashes --reinstall-package lightgbm --no-binary lightgbm --config-settings=cmake.define.USE_CUDA=ON --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=75", + uv_version="0.12.1", + ) +) + + +@app.function( + image=image, + gpu="T4", + cpu=2, + memory=8192, + timeout=900, + retries=0, + max_containers=1, + serialized=True, + include_source=False, +) +def preflight(): + import subprocess + + import cupy as cp + + spec = importlib.util.spec_from_file_location("smoke", "/opt/capability_smoke.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + # CPU-before-CUDA ordering on the same Linux installation. + cpu = module.run("cpu") + gpu = module.run("cuda") + return { + "cpu": cpu, + "cuda": gpu, + "gpu_inventory": subprocess.check_output( + ["nvidia-smi", "--query-gpu=name,driver_version,memory.total", "--format=csv,noheader"], + text=True, + ), + "cuda_runtime": cp.cuda.runtime.runtimeGetVersion(), + "cuda_driver": cp.cuda.runtime.driverGetVersion(), + } + + +@app.local_entrypoint() +def main(): + result = preflight.remote() + result.update( + source_sha=SOURCE_SHA, + dirty=DIRTY, + source_file_sha256=HASH, + lock_sha256=LOCK_HASH, + scope="comparator capability preflight only", + ) + path = ROOT / "benchmarks/v1/evidence/modal-capabilities-cuda-build.json" + path.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + for device in ["cpu", "cuda"]: + print( + device, [(c["library"], c["application"], c["status"]) for c in result[device]["cells"]] + ) + if any(c["status"] == "error" for d in ["cpu", "cuda"] for c in result[d]["cells"]): + raise RuntimeError("preflight contains failed cells; inspect saved results") diff --git a/benchmarks/v1/evidence/modal-native-abort-probe-source.txt b/benchmarks/v1/evidence/modal-native-abort-probe-source.txt new file mode 100644 index 0000000..fd6f87b --- /dev/null +++ b/benchmarks/v1/evidence/modal-native-abort-probe-source.txt @@ -0,0 +1,286 @@ +"""Installed baseline capability probes: tiny weighted fit, prediction, and reload. + +These probes are not real-data quality results or performance measurements. +Run separately on CPU and a real CUDA host; failures remain in the returned record. +""" + +import argparse +import importlib.metadata +import json +import os +import platform +import subprocess +import tempfile +import time +from pathlib import Path + +import numpy as np + + +def run(device="cpu"): + import catboost as cb + import lightgbm as lgb + import xgboost as xgb + + rng = np.random.default_rng(41) + x = rng.normal(size=(96, 5)) + y = 2 + x[:, 0] + 0.1 * rng.normal(size=96) + weights = np.where(x[:, 1] > 0, 3.0, 0.5) + binary = (x[:, 0] > 0).astype(int) + multi = np.arange(96) % 3 + positive = np.exp(y / 2) + count = rng.poisson(positive) + rounds = 4 + cells = [] + for library in ["xgboost", "lightgbm", "catboost"]: + for task in ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11"]: + start = time.perf_counter() + record = {"library": library, "application": task, "device": device, "status": "error"} + try: + if library == "catboost" and task == "A10" and device == "cuda": + record.update( + status="unsupported", + reason="SurvivalAft GPU fit rejected by version 1.2.10; retained initial evidence", + ) + continue + if ( + (library == "lightgbm" and task in ["A10", "A11"]) + or (library == "catboost" and task == "A8") + or (library == "xgboost" and task == "A11") + ): + record.update( + status="unsupported", + reason="no matching builtin; separate outer-loop control required", + ) + continue + target = { + "A2": binary, + "A3": multi, + "A4": multi, + "A6": np.column_stack([y, 2 * y]), + "A7": count, + "A8": positive, + "A9": positive, + "A10": positive, + }.get(task, y) + with tempfile.TemporaryDirectory() as temp: + model_path = Path(temp) / "model.json" + if library == "xgboost": + objectives = { + "A1": "reg:squarederror", + "A2": "binary:logistic", + "A3": "multi:softprob", + "A4": "rank:pairwise", + "A5": "reg:quantileerror", + "A6": "reg:squarederror", + "A7": "count:poisson", + "A8": "reg:gamma", + "A9": "reg:tweedie", + "A10": "survival:aft", + } + param = { + "objective": objectives[task], + "tree_method": "hist", + "device": device, + "max_depth": 2, + "eta": 0.1, + "nthread": 2, + "seed": 41, + } + d = xgb.DMatrix(x, label=target if task != "A10" else None) + if task == "A4": + d.set_group([8] * 12) + d.set_weight(np.linspace(0.5, 2, 12)) + else: + d.set_weight(weights) + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["quantile_alpha"] = 0.5 + if task == "A6": + param["multi_strategy"] = "multi_output_tree" + if task == "A9": + param["tweedie_variance_power"] = 1.5 + if task == "A10": + d.set_float_info("label_lower_bound", positive) + d.set_float_info( + "label_upper_bound", + np.where(np.arange(96) % 4 == 0, np.inf, positive), + ) + param.update( + aft_loss_distribution="normal", aft_loss_distribution_scale=1.0 + ) + model = xgb.train(param, d, num_boost_round=rounds) + # Actual build/config is recorded; CUDA host additionally validates GPU visibility. + actual = json.loads(model.save_config())["learner"]["generic_param"][ + "device" + ] + if device == "cuda" and not actual.startswith("cuda"): + raise ValueError("silent CPU fallback") + before = model.predict(d) + model.save_model(model_path) + loaded = xgb.Booster() + loaded.load_model(model_path) + loaded.set_param({"device": device}) + after = loaded.predict(d) + if device == "cuda": + loaded.set_param({"device": "cpu"}) + cpu_prediction = loaded.predict(d) + np.testing.assert_allclose(before, cpu_prediction, rtol=1e-4, atol=1e-5) + record["cpu_inference_max_abs_error"] = float( + np.max(np.abs(before - cpu_prediction)) + ) + record["reload_device"] = device + record["effective_config"] = json.loads(model.save_config()) + elif library == "lightgbm": + objectives = { + "A1": "regression", + "A2": "binary", + "A3": "multiclass", + "A4": "lambdarank", + "A5": "quantile", + "A6": "regression", + "A7": "poisson", + "A8": "gamma", + "A9": "tweedie", + } + param = { + "objective": objectives[task], + "device_type": device, + "num_leaves": 4, + "learning_rate": 0.1, + "num_threads": 2, + "min_data_in_leaf": 2, + "verbosity": -1, + "seed": 41, + } + if task == "A3": + param["num_class"] = 3 + if task == "A5": + param["alpha"] = 0.5 + if task == "A9": + param["tweedie_variance_power"] = 1.5 + targets = target.T if task == "A6" else [target] + predictions = [] + restored = [] + for t in targets: + d = lgb.Dataset( + x, + label=t, + weight=weights + if task != "A4" + else np.repeat(np.linspace(0.5, 2, 12), 8), + group=[8] * 12 if task == "A4" else None, + ) + model = lgb.train(param, d, num_boost_round=rounds) + predictions.append(model.predict(x)) + model.save_model(str(model_path)) + restored.append(lgb.Booster(model_file=str(model_path)).predict(x)) + before = np.column_stack(predictions) if task == "A6" else predictions[0] + after = np.column_stack(restored) if task == "A6" else restored[0] + record["effective_config"] = param + else: + losses = { + "A1": "RMSE", + "A2": "Logloss", + "A3": "MultiClass", + "A4": "PairLogit", + "A5": "Quantile:alpha=0.5", + "A6": "MultiRMSE", + "A7": "Poisson", + "A9": "Tweedie:variance_power=1.5", + "A10": "SurvivalAft:dist=Normal;scale=1.0", + "A11": "RMSEWithUncertainty", + } + klass = ( + cb.CatBoostClassifier + if task in ["A2", "A3"] + else cb.CatBoostRanker + if task == "A4" + else cb.CatBoostRegressor + ) + model = klass( + loss_function=losses[task], + iterations=rounds, + depth=2, + learning_rate=0.1, + thread_count=2, + random_seed=41, + task_type="GPU" if device == "cuda" else "CPU", + verbose=False, + allow_writing_files=False, + ) + if task == "A10": + target = np.column_stack( + [positive, np.where(np.arange(96) % 4 == 0, -1, positive)] + ) + if task == "A4": + d = cb.Pool( + x, + target, + group_id=np.repeat(np.arange(12), 8), + group_weight=np.repeat(np.linspace(0.5, 2, 12), 8), + ) + else: + d = cb.Pool(x, target, weight=weights) + model.fit(d) + before = ( + model.predict(d) + if task == "A4" + else model.predict(d, prediction_type="RawFormulaVal") + ) + model.save_model(str(model_path)) + loaded = klass() + loaded.load_model(str(model_path)) + after = ( + loaded.predict(d) + if task == "A4" + else loaded.predict(d, prediction_type="RawFormulaVal") + ) + record["effective_config"] = model.get_all_params() + if not np.isfinite(before).all() or len(before) != 96: + raise ValueError("invalid predictions") + np.testing.assert_allclose(before, after, rtol=1e-7, atol=1e-8) + record.update( + status="pass", + prediction_shape=list(np.shape(before)), + reload_max_abs_error=float(np.max(np.abs(before - after))), + ) + except Exception as exc: + record.update(status="error", reason=f"{type(exc).__name__}: {exc}") + finally: + record["diagnostic_wall_s"] = time.perf_counter() - start + cells.append(record) + return { + "schema": "openboost-capability-smoke-v1", + "scope": "tiny weighted builtin fit/predict/reload only; not full capability or quality gates", + "device": device, + "environment": { + "python": platform.python_version(), + "os": platform.platform(), + "cpu": platform.processor(), + "cpu_count": os.cpu_count(), + "packages": {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + }, + "cells": cells, + } + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--device", choices=["cpu", "cuda"], default="cpu") + p.add_argument("--output", type=Path, required=True) + a = p.parse_args() + result = run(a.device) + result["source_sha"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + result["dirty"] = bool(subprocess.check_output(["git", "status", "--porcelain"])) + result["source_file_sha256"] = ( + __import__("hashlib").sha256(Path(__file__).read_bytes()).hexdigest() + ) + a.output.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + print([(r["library"], r["application"], r["status"]) for r in result["cells"]]) + return int(any(r["status"] == "error" for r in result["cells"])) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/v1/evidence/modal-preflight-initial-source.txt b/benchmarks/v1/evidence/modal-preflight-initial-source.txt new file mode 100644 index 0000000..31dfd60 --- /dev/null +++ b/benchmarks/v1/evidence/modal-preflight-initial-source.txt @@ -0,0 +1,81 @@ +"""Allowlisted comparator preflight; uploads no production source or datasets.""" + +import hashlib +import importlib.util +import json +import subprocess +from pathlib import Path + +import modal + +ROOT = Path(__file__).resolve().parents[2] +SOURCE = Path(__file__).with_name("capability_smoke.py") +LOCK = Path(__file__).with_name("requirements-cuda.txt") +SOURCE_SHA = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip() +DIRTY = bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=ROOT)) +HASH = hashlib.sha256(SOURCE.read_bytes()).hexdigest() +LOCK_HASH = hashlib.sha256(LOCK.read_bytes()).hexdigest() +app = modal.App("openboost-v1-comparator-preflight") +image = ( + modal.Image.from_registry( + "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + add_python="3.12", + ) + .uv_pip_install(requirements=[str(LOCK)], extra_options="--require-hashes", uv_version="0.12.1") + .add_local_file(SOURCE, "/opt/capability_smoke.py", copy=True) + .env({"OMP_NUM_THREADS": "2", "OPENBLAS_NUM_THREADS": "2"}) +) + + +@app.function( + image=image, + gpu="T4", + cpu=2, + memory=8192, + timeout=900, + retries=0, + max_containers=1, + serialized=True, + include_source=False, +) +def preflight(): + import subprocess + + import cupy as cp + + spec = importlib.util.spec_from_file_location("smoke", "/opt/capability_smoke.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + # CPU-before-CUDA ordering on the same Linux installation. + cpu = module.run("cpu") + gpu = module.run("cuda") + return { + "cpu": cpu, + "cuda": gpu, + "gpu_inventory": subprocess.check_output( + ["nvidia-smi", "--query-gpu=name,driver_version,memory.total", "--format=csv,noheader"], + text=True, + ), + "cuda_runtime": cp.cuda.runtime.runtimeGetVersion(), + "cuda_driver": cp.cuda.runtime.driverGetVersion(), + } + + +@app.local_entrypoint() +def main(): + result = preflight.remote() + result.update( + source_sha=SOURCE_SHA, + dirty=DIRTY, + source_file_sha256=HASH, + lock_sha256=LOCK_HASH, + scope="comparator capability preflight only", + ) + path = ROOT / "benchmarks/v1/evidence/modal-capabilities.json" + path.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + for device in ["cpu", "cuda"]: + print( + device, [(c["library"], c["application"], c["status"]) for c in result[device]["cells"]] + ) + if any(c["status"] == "error" for d in ["cpu", "cuda"] for c in result[d]["cells"]): + raise RuntimeError("preflight contains failed cells; inspect saved results") diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/0/execution.json b/benchmarks/v1/evidence/multi-worker-045/A6/0/execution.json new file mode 100644 index 0000000..0b4e9a2 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/0/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "025f3f6fcd2b9e78e3c3511a855471bc4c773feb0e3ca5461799badaa3d1da12", + "predictions.npz": "a11e3dce279378670b2cb626f44c872a75f722c15387425a2153df70d53ab5ce", + "training.json": "a7b457719d1818dd97b2c2f8208b3923c9575fd8afa22c0b2fa0f9cd3d976667", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/0/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.9231058750010561 +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/0/model.bin b/benchmarks/v1/evidence/multi-worker-045/A6/0/model.bin new file mode 100644 index 0000000..17e3fe3 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/0/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "base": [2.3104456715883348e-14, -1.7818320603713023e-15], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 62.0, 65.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 75.0, 76.0, 78.0], [0.0], [8.3986, 14.515875, 20.7618125, 25.756, 30.5116875, 35.672, 41.359125, 46.858, 51.701, 56.608, 63.297, 68.39725, 73.35618749999999, 78.702125, 84.499, 91.335, 98.4049375, 105.27, 110.848125, 116.2225, 121.51, 126.49, 133.385625, 138.4, 143.48, 148.92625, 154.49, 161.2425, 167.4, 173.01625, 181.340625], [0.00219, 0.00251, 0.00272, 0.00292, 0.00313, 0.0033, 0.00345, 0.00358, 0.00373, 0.00388, 0.00403, 0.00416, 0.00432, 0.00448, 0.00463, 0.00482, 0.00501, 0.00517, 0.005388125, 0.00565, 0.00587, 0.00614, 0.00645, 0.006755000000000001, 0.00714, 0.00752375, 0.00806, 0.008712500000000001, 0.009591875, 0.01102625, 0.013992500000000001], [1.091e-05, 1.368625e-05, 1.566e-05, 1.72775e-05, 1.8436875e-05, 1.96925e-05, 2.1026875e-05, 2.233e-05, 2.354e-05, 2.475375e-05, 2.614e-05, 2.733e-05, 2.835e-05, 2.979125e-05, 3.1010625e-05, 3.234e-05, 3.384e-05, 3.5497500000000004e-05, 3.74825e-05, 3.9275e-05, 4.109e-05, 4.321875e-05, 4.585125e-05, 4.8415e-05, 5.2033124999999997e-05, 5.618125e-05, 6.019625e-05, 6.54175e-05, 7.233625e-05, 8.439625e-05, 0.00010380249999999999], [0.00091, 0.00106, 0.00118, 0.00126, 0.00135, 0.00142, 0.00149, 0.00157, 0.00165, 0.00173, 0.0018, 0.00188, 0.00196, 0.00206, 0.00214, 0.00223, 0.00234, 0.00245, 0.00256, 0.0027, 0.00282, 0.00295625, 0.00309, 0.00326, 0.00346, 0.00372375, 0.003983125, 0.00425, 0.00475, 0.00565, 0.00729125], [0.0011093750000000001, 0.0013, 0.00141, 0.00151, 0.00161, 0.00168, 0.00175, 0.00184, 0.00192, 0.00198, 0.00205, 0.00212, 0.00219, 0.00226, 0.00235, 0.00244, 0.00253, 0.00264, 0.00276, 0.0028675000000000003, 0.003, 0.00313, 0.003275625, 0.00342, 0.0036, 0.00388, 0.004163125, 0.00453, 0.005, 0.0056949999999999995, 0.007110625], [0.00273, 0.00318, 0.00354, 0.00378, 0.00404, 0.00427, 0.00448, 0.004705, 0.00496, 0.0052, 0.00541, 0.00565, 0.00589, 0.00617, 0.00641, 0.00669, 0.00701, 0.00734, 0.00769, 0.0080975, 0.00847, 0.008862499999999999, 0.00926, 0.009774999999999999, 0.01038, 0.0111675, 0.011953125, 0.0127525, 0.014251875, 0.01695, 0.021874375], [0.01178875, 0.013908750000000001, 0.015308125, 0.0164575, 0.01762, 0.01864875, 0.01964, 0.02061, 0.021604375000000002, 0.0226675, 0.02349, 0.024454999999999998, 0.02527, 0.02643125, 0.02743, 0.02839, 0.029499374999999998, 0.03088625, 0.032228125, 0.0333775, 0.03480375, 0.03640625, 0.03811, 0.039885000000000004, 0.04218, 0.04444125, 0.0476225, 0.0510025, 0.055156875, 0.061555, 0.07409187499999999], [0.109, 0.128, 0.141, 0.15175, 0.161, 0.17, 0.179, 0.188, 0.197, 0.206, 0.213, 0.223, 0.233, 0.242, 0.253, 0.261, 0.273, 0.283, 0.295, 0.306, 0.32, 0.334, 0.3485625, 0.365, 0.381, 0.406375, 0.437, 0.467, 0.5021875, 0.569, 0.6811250000000001], [0.00524, 0.00636, 0.00721, 0.0078575, 0.00838, 0.00898, 0.00946, 0.009955, 0.01048, 0.01098375, 0.01156, 0.012, 0.01242375, 0.01302125, 0.013610625000000001, 0.01416, 0.014769375, 0.01550625, 0.01618625, 0.01687, 0.01772, 0.0185625, 0.01967, 0.02066, 0.021898749999999998, 0.0234, 0.024943125, 0.026785000000000003, 0.029128125, 0.03290125, 0.040111875], [0.006339375, 0.007648749999999999, 0.008488125, 0.0093075, 0.00998, 0.01054, 0.011175625, 0.011715, 0.01221875, 0.01287375, 0.013443125, 0.014, 0.0146, 0.01514, 0.015720625000000002, 0.01649, 0.017189375, 0.01785, 0.018628125000000002, 0.0195175, 0.0205, 0.02137, 0.02242, 0.023684999999999998, 0.02503, 0.02642375, 0.02847, 0.0304725, 0.033271875, 0.0368775, 0.044901875], [0.009327499999999999, 0.011067500000000001, 0.012368125, 0.01333, 0.01429, 0.01523625, 0.01603, 0.01689, 0.017684375000000002, 0.01852375, 0.01925, 0.0200825, 0.02096, 0.02183125, 0.02259, 0.02345, 0.024399375, 0.02526, 0.026268125, 0.02727, 0.028566875000000002, 0.0297925, 0.031275625, 0.032775, 0.034420624999999996, 0.03604, 0.038209375, 0.0411825, 0.04501, 0.05072, 0.061569374999999996], [0.01573, 0.01909, 0.021628125, 0.02358, 0.02514, 0.02694, 0.02838, 0.029865000000000003, 0.031444375000000004, 0.032953750000000004, 0.03469, 0.0359925, 0.037279375, 0.03907125, 0.040840625, 0.04247, 0.044317499999999996, 0.04651125, 0.0485525, 0.0506075, 0.05315, 0.055691250000000005, 0.05902, 0.061975, 0.06569187500000001, 0.07019375, 0.0748225, 0.08036, 0.0873925, 0.09871375, 0.120326875], [0.0033205625, 0.004699, 0.005985750000000001, 0.00712, 0.008145375, 0.009166, 0.0100145625, 0.0110075, 0.0119601875, 0.012912874999999999, 0.0137411875, 0.0148135, 0.0158904375, 0.01698575, 0.0179055625, 0.018978, 0.0201028125, 0.021484, 0.0228538125, 0.02430075, 0.026208125000000002, 0.02799275, 0.0300216875, 0.032231499999999996, 0.0348035625, 0.037176125000000004, 0.040763562499999996, 0.045691, 0.051214, 0.062017875, 0.0896069375], [14.733625, 15.835, 16.8325, 17.51225, 18.092750000000002, 18.591375, 19.0140625, 19.389499999999998, 19.7648125, 20.108375000000002, 20.388875, 20.7235, 21.0131875, 21.34375, 21.6340625, 21.948, 22.210874999999998, 22.469875, 22.753625, 23.06875, 23.387062500000003, 23.722875000000002, 24.066375, 24.424500000000002, 24.7354375, 25.128875, 25.500249999999998, 25.998, 26.683, 27.48, 28.9064375], [0.3497225, 0.38797875000000004, 0.40764062500000003, 0.4249375, 0.434869375, 0.44831375, 0.45879625, 0.46862000000000004, 0.479474375, 0.49061374999999996, 0.4990875, 0.507105, 0.515115625, 0.52418125, 0.5321575000000001, 0.53941, 0.5470275, 0.55536875, 0.562794375, 0.5705724999999999, 0.5786375, 0.5877325, 0.5968181250000001, 0.6064849999999999, 0.614514375, 0.6245499999999999, 0.635410625, 0.6474075, 0.659594375, 0.67484875, 0.69947375], [0.539710625, 0.55062, 0.559010625, 0.56806, 0.5748275, 0.5816662499999999, 0.588489375, 0.594775, 0.60128625, 0.60709, 0.61180875, 0.6171899999999999, 0.6214737499999999, 0.62674125, 0.633190625, 0.63861, 0.64380625, 0.6497225, 0.65648625, 0.6641925, 0.673486875, 0.68272875, 0.6933675, 0.70377, 0.713385625, 0.72425625, 0.73371625, 0.7434350000000001, 0.753645, 0.76485625, 0.78364125], [0.0961196875, 0.11177000000000001, 0.12459187499999999, 0.13399, 0.14121, 0.14765875, 0.15419875, 0.160525, 0.166294375, 0.17081875, 0.1759375, 0.18202000000000002, 0.18751375, 0.19238, 0.19865375, 0.20437, 0.210224375, 0.215415, 0.2213725, 0.2283375, 0.235841875, 0.24218125000000001, 0.250714375, 0.25943499999999997, 0.2701225, 0.282115, 0.294169375, 0.312915, 0.33143, 0.35260375, 0.39347062499999996], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [7, 1, 16, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.9925447632320756e-17, -1.905522121336285e-15], [-0.6539156228836387, -0.6524060587433276], [0.5622419508056056, 0.5609440153267453], [0.13545407260321393, 0.10975410755252714], [-0.9204238485351731, -0.9097103222507609], [0.6599118932316349, 0.630998120479098], [-0.626023238827566, -0.29244593117331436]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 62.0, 65.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 75.0, 76.0, 78.0], [0.0], [8.3986, 14.515875, 20.7618125, 25.756, 30.5116875, 35.672, 41.359125, 46.858, 51.701, 56.608, 63.297, 68.39725, 73.35618749999999, 78.702125, 84.499, 91.335, 98.4049375, 105.27, 110.848125, 116.2225, 121.51, 126.49, 133.385625, 138.4, 143.48, 148.92625, 154.49, 161.2425, 167.4, 173.01625, 181.340625], [0.00219, 0.00251, 0.00272, 0.00292, 0.00313, 0.0033, 0.00345, 0.00358, 0.00373, 0.00388, 0.00403, 0.00416, 0.00432, 0.00448, 0.00463, 0.00482, 0.00501, 0.00517, 0.005388125, 0.00565, 0.00587, 0.00614, 0.00645, 0.006755000000000001, 0.00714, 0.00752375, 0.00806, 0.008712500000000001, 0.009591875, 0.01102625, 0.013992500000000001], [1.091e-05, 1.368625e-05, 1.566e-05, 1.72775e-05, 1.8436875e-05, 1.96925e-05, 2.1026875e-05, 2.233e-05, 2.354e-05, 2.475375e-05, 2.614e-05, 2.733e-05, 2.835e-05, 2.979125e-05, 3.1010625e-05, 3.234e-05, 3.384e-05, 3.5497500000000004e-05, 3.74825e-05, 3.9275e-05, 4.109e-05, 4.321875e-05, 4.585125e-05, 4.8415e-05, 5.2033124999999997e-05, 5.618125e-05, 6.019625e-05, 6.54175e-05, 7.233625e-05, 8.439625e-05, 0.00010380249999999999], [0.00091, 0.00106, 0.00118, 0.00126, 0.00135, 0.00142, 0.00149, 0.00157, 0.00165, 0.00173, 0.0018, 0.00188, 0.00196, 0.00206, 0.00214, 0.00223, 0.00234, 0.00245, 0.00256, 0.0027, 0.00282, 0.00295625, 0.00309, 0.00326, 0.00346, 0.00372375, 0.003983125, 0.00425, 0.00475, 0.00565, 0.00729125], [0.0011093750000000001, 0.0013, 0.00141, 0.00151, 0.00161, 0.00168, 0.00175, 0.00184, 0.00192, 0.00198, 0.00205, 0.00212, 0.00219, 0.00226, 0.00235, 0.00244, 0.00253, 0.00264, 0.00276, 0.0028675000000000003, 0.003, 0.00313, 0.003275625, 0.00342, 0.0036, 0.00388, 0.004163125, 0.00453, 0.005, 0.0056949999999999995, 0.007110625], [0.00273, 0.00318, 0.00354, 0.00378, 0.00404, 0.00427, 0.00448, 0.004705, 0.00496, 0.0052, 0.00541, 0.00565, 0.00589, 0.00617, 0.00641, 0.00669, 0.00701, 0.00734, 0.00769, 0.0080975, 0.00847, 0.008862499999999999, 0.00926, 0.009774999999999999, 0.01038, 0.0111675, 0.011953125, 0.0127525, 0.014251875, 0.01695, 0.021874375], [0.01178875, 0.013908750000000001, 0.015308125, 0.0164575, 0.01762, 0.01864875, 0.01964, 0.02061, 0.021604375000000002, 0.0226675, 0.02349, 0.024454999999999998, 0.02527, 0.02643125, 0.02743, 0.02839, 0.029499374999999998, 0.03088625, 0.032228125, 0.0333775, 0.03480375, 0.03640625, 0.03811, 0.039885000000000004, 0.04218, 0.04444125, 0.0476225, 0.0510025, 0.055156875, 0.061555, 0.07409187499999999], [0.109, 0.128, 0.141, 0.15175, 0.161, 0.17, 0.179, 0.188, 0.197, 0.206, 0.213, 0.223, 0.233, 0.242, 0.253, 0.261, 0.273, 0.283, 0.295, 0.306, 0.32, 0.334, 0.3485625, 0.365, 0.381, 0.406375, 0.437, 0.467, 0.5021875, 0.569, 0.6811250000000001], [0.00524, 0.00636, 0.00721, 0.0078575, 0.00838, 0.00898, 0.00946, 0.009955, 0.01048, 0.01098375, 0.01156, 0.012, 0.01242375, 0.01302125, 0.013610625000000001, 0.01416, 0.014769375, 0.01550625, 0.01618625, 0.01687, 0.01772, 0.0185625, 0.01967, 0.02066, 0.021898749999999998, 0.0234, 0.024943125, 0.026785000000000003, 0.029128125, 0.03290125, 0.040111875], [0.006339375, 0.007648749999999999, 0.008488125, 0.0093075, 0.00998, 0.01054, 0.011175625, 0.011715, 0.01221875, 0.01287375, 0.013443125, 0.014, 0.0146, 0.01514, 0.015720625000000002, 0.01649, 0.017189375, 0.01785, 0.018628125000000002, 0.0195175, 0.0205, 0.02137, 0.02242, 0.023684999999999998, 0.02503, 0.02642375, 0.02847, 0.0304725, 0.033271875, 0.0368775, 0.044901875], [0.009327499999999999, 0.011067500000000001, 0.012368125, 0.01333, 0.01429, 0.01523625, 0.01603, 0.01689, 0.017684375000000002, 0.01852375, 0.01925, 0.0200825, 0.02096, 0.02183125, 0.02259, 0.02345, 0.024399375, 0.02526, 0.026268125, 0.02727, 0.028566875000000002, 0.0297925, 0.031275625, 0.032775, 0.034420624999999996, 0.03604, 0.038209375, 0.0411825, 0.04501, 0.05072, 0.061569374999999996], [0.01573, 0.01909, 0.021628125, 0.02358, 0.02514, 0.02694, 0.02838, 0.029865000000000003, 0.031444375000000004, 0.032953750000000004, 0.03469, 0.0359925, 0.037279375, 0.03907125, 0.040840625, 0.04247, 0.044317499999999996, 0.04651125, 0.0485525, 0.0506075, 0.05315, 0.055691250000000005, 0.05902, 0.061975, 0.06569187500000001, 0.07019375, 0.0748225, 0.08036, 0.0873925, 0.09871375, 0.120326875], [0.0033205625, 0.004699, 0.005985750000000001, 0.00712, 0.008145375, 0.009166, 0.0100145625, 0.0110075, 0.0119601875, 0.012912874999999999, 0.0137411875, 0.0148135, 0.0158904375, 0.01698575, 0.0179055625, 0.018978, 0.0201028125, 0.021484, 0.0228538125, 0.02430075, 0.026208125000000002, 0.02799275, 0.0300216875, 0.032231499999999996, 0.0348035625, 0.037176125000000004, 0.040763562499999996, 0.045691, 0.051214, 0.062017875, 0.0896069375], [14.733625, 15.835, 16.8325, 17.51225, 18.092750000000002, 18.591375, 19.0140625, 19.389499999999998, 19.7648125, 20.108375000000002, 20.388875, 20.7235, 21.0131875, 21.34375, 21.6340625, 21.948, 22.210874999999998, 22.469875, 22.753625, 23.06875, 23.387062500000003, 23.722875000000002, 24.066375, 24.424500000000002, 24.7354375, 25.128875, 25.500249999999998, 25.998, 26.683, 27.48, 28.9064375], [0.3497225, 0.38797875000000004, 0.40764062500000003, 0.4249375, 0.434869375, 0.44831375, 0.45879625, 0.46862000000000004, 0.479474375, 0.49061374999999996, 0.4990875, 0.507105, 0.515115625, 0.52418125, 0.5321575000000001, 0.53941, 0.5470275, 0.55536875, 0.562794375, 0.5705724999999999, 0.5786375, 0.5877325, 0.5968181250000001, 0.6064849999999999, 0.614514375, 0.6245499999999999, 0.635410625, 0.6474075, 0.659594375, 0.67484875, 0.69947375], [0.539710625, 0.55062, 0.559010625, 0.56806, 0.5748275, 0.5816662499999999, 0.588489375, 0.594775, 0.60128625, 0.60709, 0.61180875, 0.6171899999999999, 0.6214737499999999, 0.62674125, 0.633190625, 0.63861, 0.64380625, 0.6497225, 0.65648625, 0.6641925, 0.673486875, 0.68272875, 0.6933675, 0.70377, 0.713385625, 0.72425625, 0.73371625, 0.7434350000000001, 0.753645, 0.76485625, 0.78364125], [0.0961196875, 0.11177000000000001, 0.12459187499999999, 0.13399, 0.14121, 0.14765875, 0.15419875, 0.160525, 0.166294375, 0.17081875, 0.1759375, 0.18202000000000002, 0.18751375, 0.19238, 0.19865375, 0.20437, 0.210224375, 0.215415, 0.2213725, 0.2283375, 0.235841875, 0.24218125000000001, 0.250714375, 0.25943499999999997, 0.2701225, 0.282115, 0.294169375, 0.312915, 0.33143, 0.35260375, 0.39347062499999996], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [7, 1, 16, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-2.1533289035395002e-05, -1.3228326418159708e-05], [-0.5885727258014688, -0.5872150471786463], [0.5060195621565132, 0.5048676602860378], [0.12194186487049082, 0.0988055973138317], [-0.8284577840671085, -0.8188147220590377], [0.5939587611110764, 0.5679346981728556], [-0.5638586934335, -0.2634058457001602]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 62.0, 65.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 75.0, 76.0, 78.0], [0.0], [8.3986, 14.515875, 20.7618125, 25.756, 30.5116875, 35.672, 41.359125, 46.858, 51.701, 56.608, 63.297, 68.39725, 73.35618749999999, 78.702125, 84.499, 91.335, 98.4049375, 105.27, 110.848125, 116.2225, 121.51, 126.49, 133.385625, 138.4, 143.48, 148.92625, 154.49, 161.2425, 167.4, 173.01625, 181.340625], [0.00219, 0.00251, 0.00272, 0.00292, 0.00313, 0.0033, 0.00345, 0.00358, 0.00373, 0.00388, 0.00403, 0.00416, 0.00432, 0.00448, 0.00463, 0.00482, 0.00501, 0.00517, 0.005388125, 0.00565, 0.00587, 0.00614, 0.00645, 0.006755000000000001, 0.00714, 0.00752375, 0.00806, 0.008712500000000001, 0.009591875, 0.01102625, 0.013992500000000001], [1.091e-05, 1.368625e-05, 1.566e-05, 1.72775e-05, 1.8436875e-05, 1.96925e-05, 2.1026875e-05, 2.233e-05, 2.354e-05, 2.475375e-05, 2.614e-05, 2.733e-05, 2.835e-05, 2.979125e-05, 3.1010625e-05, 3.234e-05, 3.384e-05, 3.5497500000000004e-05, 3.74825e-05, 3.9275e-05, 4.109e-05, 4.321875e-05, 4.585125e-05, 4.8415e-05, 5.2033124999999997e-05, 5.618125e-05, 6.019625e-05, 6.54175e-05, 7.233625e-05, 8.439625e-05, 0.00010380249999999999], [0.00091, 0.00106, 0.00118, 0.00126, 0.00135, 0.00142, 0.00149, 0.00157, 0.00165, 0.00173, 0.0018, 0.00188, 0.00196, 0.00206, 0.00214, 0.00223, 0.00234, 0.00245, 0.00256, 0.0027, 0.00282, 0.00295625, 0.00309, 0.00326, 0.00346, 0.00372375, 0.003983125, 0.00425, 0.00475, 0.00565, 0.00729125], [0.0011093750000000001, 0.0013, 0.00141, 0.00151, 0.00161, 0.00168, 0.00175, 0.00184, 0.00192, 0.00198, 0.00205, 0.00212, 0.00219, 0.00226, 0.00235, 0.00244, 0.00253, 0.00264, 0.00276, 0.0028675000000000003, 0.003, 0.00313, 0.003275625, 0.00342, 0.0036, 0.00388, 0.004163125, 0.00453, 0.005, 0.0056949999999999995, 0.007110625], [0.00273, 0.00318, 0.00354, 0.00378, 0.00404, 0.00427, 0.00448, 0.004705, 0.00496, 0.0052, 0.00541, 0.00565, 0.00589, 0.00617, 0.00641, 0.00669, 0.00701, 0.00734, 0.00769, 0.0080975, 0.00847, 0.008862499999999999, 0.00926, 0.009774999999999999, 0.01038, 0.0111675, 0.011953125, 0.0127525, 0.014251875, 0.01695, 0.021874375], [0.01178875, 0.013908750000000001, 0.015308125, 0.0164575, 0.01762, 0.01864875, 0.01964, 0.02061, 0.021604375000000002, 0.0226675, 0.02349, 0.024454999999999998, 0.02527, 0.02643125, 0.02743, 0.02839, 0.029499374999999998, 0.03088625, 0.032228125, 0.0333775, 0.03480375, 0.03640625, 0.03811, 0.039885000000000004, 0.04218, 0.04444125, 0.0476225, 0.0510025, 0.055156875, 0.061555, 0.07409187499999999], [0.109, 0.128, 0.141, 0.15175, 0.161, 0.17, 0.179, 0.188, 0.197, 0.206, 0.213, 0.223, 0.233, 0.242, 0.253, 0.261, 0.273, 0.283, 0.295, 0.306, 0.32, 0.334, 0.3485625, 0.365, 0.381, 0.406375, 0.437, 0.467, 0.5021875, 0.569, 0.6811250000000001], [0.00524, 0.00636, 0.00721, 0.0078575, 0.00838, 0.00898, 0.00946, 0.009955, 0.01048, 0.01098375, 0.01156, 0.012, 0.01242375, 0.01302125, 0.013610625000000001, 0.01416, 0.014769375, 0.01550625, 0.01618625, 0.01687, 0.01772, 0.0185625, 0.01967, 0.02066, 0.021898749999999998, 0.0234, 0.024943125, 0.026785000000000003, 0.029128125, 0.03290125, 0.040111875], [0.006339375, 0.007648749999999999, 0.008488125, 0.0093075, 0.00998, 0.01054, 0.011175625, 0.011715, 0.01221875, 0.01287375, 0.013443125, 0.014, 0.0146, 0.01514, 0.015720625000000002, 0.01649, 0.017189375, 0.01785, 0.018628125000000002, 0.0195175, 0.0205, 0.02137, 0.02242, 0.023684999999999998, 0.02503, 0.02642375, 0.02847, 0.0304725, 0.033271875, 0.0368775, 0.044901875], [0.009327499999999999, 0.011067500000000001, 0.012368125, 0.01333, 0.01429, 0.01523625, 0.01603, 0.01689, 0.017684375000000002, 0.01852375, 0.01925, 0.0200825, 0.02096, 0.02183125, 0.02259, 0.02345, 0.024399375, 0.02526, 0.026268125, 0.02727, 0.028566875000000002, 0.0297925, 0.031275625, 0.032775, 0.034420624999999996, 0.03604, 0.038209375, 0.0411825, 0.04501, 0.05072, 0.061569374999999996], [0.01573, 0.01909, 0.021628125, 0.02358, 0.02514, 0.02694, 0.02838, 0.029865000000000003, 0.031444375000000004, 0.032953750000000004, 0.03469, 0.0359925, 0.037279375, 0.03907125, 0.040840625, 0.04247, 0.044317499999999996, 0.04651125, 0.0485525, 0.0506075, 0.05315, 0.055691250000000005, 0.05902, 0.061975, 0.06569187500000001, 0.07019375, 0.0748225, 0.08036, 0.0873925, 0.09871375, 0.120326875], [0.0033205625, 0.004699, 0.005985750000000001, 0.00712, 0.008145375, 0.009166, 0.0100145625, 0.0110075, 0.0119601875, 0.012912874999999999, 0.0137411875, 0.0148135, 0.0158904375, 0.01698575, 0.0179055625, 0.018978, 0.0201028125, 0.021484, 0.0228538125, 0.02430075, 0.026208125000000002, 0.02799275, 0.0300216875, 0.032231499999999996, 0.0348035625, 0.037176125000000004, 0.040763562499999996, 0.045691, 0.051214, 0.062017875, 0.0896069375], [14.733625, 15.835, 16.8325, 17.51225, 18.092750000000002, 18.591375, 19.0140625, 19.389499999999998, 19.7648125, 20.108375000000002, 20.388875, 20.7235, 21.0131875, 21.34375, 21.6340625, 21.948, 22.210874999999998, 22.469875, 22.753625, 23.06875, 23.387062500000003, 23.722875000000002, 24.066375, 24.424500000000002, 24.7354375, 25.128875, 25.500249999999998, 25.998, 26.683, 27.48, 28.9064375], [0.3497225, 0.38797875000000004, 0.40764062500000003, 0.4249375, 0.434869375, 0.44831375, 0.45879625, 0.46862000000000004, 0.479474375, 0.49061374999999996, 0.4990875, 0.507105, 0.515115625, 0.52418125, 0.5321575000000001, 0.53941, 0.5470275, 0.55536875, 0.562794375, 0.5705724999999999, 0.5786375, 0.5877325, 0.5968181250000001, 0.6064849999999999, 0.614514375, 0.6245499999999999, 0.635410625, 0.6474075, 0.659594375, 0.67484875, 0.69947375], [0.539710625, 0.55062, 0.559010625, 0.56806, 0.5748275, 0.5816662499999999, 0.588489375, 0.594775, 0.60128625, 0.60709, 0.61180875, 0.6171899999999999, 0.6214737499999999, 0.62674125, 0.633190625, 0.63861, 0.64380625, 0.6497225, 0.65648625, 0.6641925, 0.673486875, 0.68272875, 0.6933675, 0.70377, 0.713385625, 0.72425625, 0.73371625, 0.7434350000000001, 0.753645, 0.76485625, 0.78364125], [0.0961196875, 0.11177000000000001, 0.12459187499999999, 0.13399, 0.14121, 0.14765875, 0.15419875, 0.160525, 0.166294375, 0.17081875, 0.1759375, 0.18202000000000002, 0.18751375, 0.19238, 0.19865375, 0.20437, 0.210224375, 0.215415, 0.2213725, 0.2283375, 0.235841875, 0.24218125000000001, 0.250714375, 0.25943499999999997, 0.2701225, 0.282115, 0.294169375, 0.312915, 0.33143, 0.35260375, 0.39347062499999996], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [7, 1, 15, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-3.877261642442755e-05, -2.3817198828767614e-05], [-0.52975925458023, -0.5285381803482401], [0.455419210422083, 0.45439712713869707], [0.10977756609541946, 0.08894925464061341], [-0.7456807003356909, -0.7370011449378792], [0.5698533512034203, 0.5557817408346989], [-0.1222633737386821, -0.05757297631814203]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [21.832476455405597, 29.161076799541192], "std": [8.446609478513288, 10.891162854284502], "constant": [false, false]}} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/0/predictions.npz b/benchmarks/v1/evidence/multi-worker-045/A6/0/predictions.npz new file mode 100644 index 0000000..118d3da Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/0/predictions.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/0/replay.npz b/benchmarks/v1/evidence/multi-worker-045/A6/0/replay.npz new file mode 100644 index 0000000..118d3da Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/0/replay.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/0/training.json b/benchmarks/v1/evidence/multi-worker-045/A6/0/training.json new file mode 100644 index 0000000..226e33b --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/0/training.json @@ -0,0 +1,33 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.0140254110026818, + "last_score": 1.0203684285829633, + "completed_rounds": 4, + "stale_rounds": 1, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "15d2bb99b467704780a70bdacccb031c65b634e6ac6080b0db8b04a6406afe3f", + "best_validation_score": 1.0140254110026818, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/0/worker.log b/benchmarks/v1/evidence/multi-worker-045/A6/0/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/1/execution.json b/benchmarks/v1/evidence/multi-worker-045/A6/1/execution.json new file mode 100644 index 0000000..aa70beb --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/1/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "97ab141747fef1f1794d4a832fc130e03070ad13b00309170b766cfc534a8a06", + "predictions.npz": "bd2b24fc742b886f71ad6fe82676e1e8d8baba63a5b21410aebab450063b6d45", + "training.json": "539c657a4479ccccea143a5727721ca85c61a2cde18cee0cee49eac8ce92f57c", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/1/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.913862916000653 +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/1/model.bin b/benchmarks/v1/evidence/multi-worker-045/A6/1/model.bin new file mode 100644 index 0000000..1423cac --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/1/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "base": [1.1994094953338141e-14, -6.126284375629343e-15], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[49.0, 57.0, 58.0, 59.0, 60.0, 62.0, 65.0, 66.0, 67.0, 68.0, 71.0, 72.0, 73.0, 74.0, 75.0, 76.0, 78.0], [0.0], [8.3981625, 13.402750000000001, 19.429125, 24.81, 29.383875, 34.801, 40.773125, 46.773, 51.419, 55.362, 61.360125, 67.432, 73.354, 78.601, 84.303625, 91.332, 97.326, 104.38, 110.71, 117.37, 122.55, 127.36, 133.32, 138.87, 144.9, 150.55, 156.71, 161.87, 167.34, 174.365, 181.34625], [0.00222, 0.00255, 0.00284, 0.00306, 0.00329, 0.00349, 0.0036862500000000003, 0.00385, 0.00399, 0.00413, 0.0043, 0.00444, 0.0046, 0.00477, 0.00496625, 0.00513, 0.00529, 0.00545, 0.00565, 0.00583, 0.00602, 0.00626, 0.00653, 0.00687, 0.00719, 0.00758, 0.00802, 0.008565, 0.00941875, 0.010535, 0.01318625], [1.037375e-05, 1.326e-05, 1.616125e-05, 1.843e-05, 2.061125e-05, 2.23625e-05, 2.404625e-05, 2.573e-05, 2.732375e-05, 2.892e-05, 3.06525e-05, 3.2065e-05, 3.34775e-05, 3.51225e-05, 3.686125e-05, 3.846e-05, 4.015125e-05, 4.176e-05, 4.363125e-05, 4.582e-05, 4.776e-05, 5.0482499999999997e-05, 5.28925e-05, 5.55e-05, 5.8533749999999995e-05, 6.195e-05, 6.583125e-05, 7.091500000000001e-05, 7.871375e-05, 8.824e-05, 0.00010617625], [0.00093, 0.00109, 0.0012, 0.00131, 0.0014, 0.00149, 0.00157, 0.00167, 0.00176, 0.00184, 0.00192, 0.002, 0.00208, 0.00216, 0.00224, 0.00233, 0.00243, 0.00254, 0.00265, 0.00276, 0.00288, 0.00298, 0.00312, 0.00329, 0.00348, 0.00372, 0.00396, 0.00422, 0.00465, 0.005325, 0.00707], [0.00111, 0.0013, 0.00142125, 0.00156, 0.00167, 0.0017625000000000002, 0.00186, 0.00196, 0.00204375, 0.00212, 0.00219, 0.00227, 0.00236, 0.00245, 0.00254, 0.00264, 0.00274, 0.0028375, 0.00293, 0.00302, 0.00313, 0.00325, 0.00337, 0.00353, 0.00371, 0.00393, 0.00421, 0.004505, 0.0049187499999999995, 0.00557, 0.007072500000000001], [0.00278, 0.00328, 0.00361, 0.00393, 0.004198749999999999, 0.00446, 0.00471625, 0.005, 0.00527, 0.00552, 0.00576, 0.00599, 0.00623, 0.00647, 0.00672, 0.00699, 0.0073, 0.00761, 0.00794, 0.00828, 0.00863, 0.00895, 0.00937, 0.00987, 0.010437499999999999, 0.01115, 0.01187125, 0.01266, 0.01396, 0.01598, 0.02121625], [0.01178125, 0.01346, 0.01485375, 0.01603, 0.0171475, 0.0181525, 0.0193325, 0.02036, 0.0213975, 0.02246, 0.02349125, 0.024454999999999998, 0.02533, 0.026565, 0.02766625, 0.02864, 0.02993375, 0.03116, 0.03232125, 0.033805, 0.03512875, 0.0365725, 0.03835625, 0.04033, 0.04259375, 0.0452375, 0.048378750000000005, 0.05172, 0.05567625, 0.062375, 0.07508875], [0.107, 0.124, 0.136, 0.147, 0.157, 0.168, 0.176, 0.186, 0.196, 0.205, 0.214, 0.224, 0.234, 0.243, 0.253625, 0.263, 0.274, 0.286, 0.298, 0.309, 0.322, 0.334, 0.351, 0.368, 0.386, 0.412, 0.442, 0.471, 0.509, 0.5692499999999999, 0.68225], [0.004793749999999999, 0.0059, 0.00676, 0.00737, 0.00805, 0.00864, 0.00923, 0.00978, 0.010267499999999999, 0.0107975, 0.01132, 0.01192, 0.01241, 0.01295, 0.01354625, 0.01412, 0.014673750000000001, 0.0153, 0.01605, 0.016704999999999998, 0.017507500000000002, 0.01837, 0.01955, 0.02057, 0.02177, 0.0234075, 0.02498125, 0.026905, 0.0294075, 0.032780000000000004, 0.03952], [0.0061125, 0.0072725, 0.00818125, 0.008935, 0.00962, 0.01021, 0.0108725, 0.01153, 0.01208, 0.0128675, 0.01345, 0.01405, 0.01464, 0.015295, 0.01595, 0.01676, 0.01746375, 0.0182275, 0.018891250000000002, 0.01973, 0.02081, 0.0217075, 0.02286, 0.024, 0.02555375, 0.0270175, 0.02905625, 0.03113, 0.03380875, 0.0380325, 0.046630000000000005], [0.00947125, 0.0110175, 0.01226, 0.013315, 0.01425875, 0.01527, 0.01608, 0.01696, 0.01796, 0.01877, 0.0196325, 0.02039, 0.02131, 0.02225, 0.0230825, 0.02406, 0.02506375, 0.0259825, 0.02705, 0.028135, 0.02943875, 0.0308425, 0.03221625, 0.03355, 0.03513375, 0.037235000000000004, 0.03965125, 0.04231, 0.04640875, 0.0522425, 0.0630025], [0.01438125, 0.0176975, 0.02028125, 0.02211, 0.02415875, 0.0259225, 0.02769, 0.02934, 0.0307925, 0.032395, 0.03396125, 0.035765000000000005, 0.03722875, 0.038845, 0.04063625, 0.04236, 0.0440175, 0.045907500000000004, 0.04815125, 0.050115, 0.05251375, 0.0551025, 0.05865, 0.06172, 0.0653075, 0.0702275, 0.0749525, 0.08072, 0.08821375000000001, 0.09834000000000001, 0.11855625], [0.0035092499999999998, 0.005154, 0.006539125000000001, 0.0079505, 0.008926374999999999, 0.0098545, 0.010946625, 0.011805, 0.012621375, 0.01344425, 0.014347249999999999, 0.015273499999999999, 0.01618875, 0.01706825, 0.017928375, 0.018959, 0.019922875, 0.021121249999999998, 0.0225605, 0.023973500000000002, 0.0254205, 0.02724725, 0.029131125, 0.03135, 0.033487750000000004, 0.03616325, 0.039170250000000004, 0.04353, 0.04906925, 0.057689, 0.081698875], [14.784125, 15.871, 16.895249999999997, 17.54, 18.076625, 18.48525, 18.89275, 19.202, 19.513, 19.831, 20.079125, 20.307499999999997, 20.60175, 20.87625, 21.17425, 21.441, 21.697375, 21.9675, 22.256125, 22.5375, 22.787875, 23.15525, 23.471375, 23.879, 24.28175, 24.73075, 25.188375, 25.6435, 26.340125, 27.294249999999998, 28.580125], [0.3656, 0.40079000000000004, 0.42074125, 0.434515, 0.449665, 0.46168750000000003, 0.47263625, 0.48616, 0.49689625, 0.5053725, 0.514085, 0.522705, 0.5321575, 0.5405025, 0.54852375, 0.55739, 0.56595625, 0.5738624999999999, 0.582065, 0.59219, 0.6007687500000001, 0.6077125, 0.61561625, 0.62539, 0.63387625, 0.6429275, 0.65359625, 0.663305, 0.6745175, 0.6905175, 0.7086], [0.5517562500000001, 0.567645, 0.57597375, 0.58236, 0.58974, 0.5963325, 0.6030525, 0.60923, 0.615665, 0.621215, 0.62758625, 0.63573, 0.64331625, 0.6510325, 0.65966625, 0.6696, 0.67786125, 0.6857449999999999, 0.6945112499999999, 0.70208, 0.707765, 0.7139449999999999, 0.719565, 0.72594, 0.732625, 0.7386175, 0.74567625, 0.7542, 0.76462875, 0.777065, 0.7952075000000001], [0.094572, 0.1121075, 0.12583, 0.1366, 0.14491375, 0.1531475, 0.16088124999999998, 0.16674, 0.17163875, 0.177875, 0.18398, 0.18853999999999999, 0.19392874999999998, 0.2005225, 0.20673625, 0.21238, 0.21761375, 0.224305, 0.230635, 0.23708, 0.24402, 0.249895, 0.2571775, 0.26461, 0.2744375, 0.285115, 0.29847375, 0.31344, 0.33233875, 0.35729500000000003, 0.4048], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [8, 0, 10, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-2.6674883141177713e-15, 2.64091081896121e-15], [-0.5507780840111293, -0.5508843217670932], [0.5498440341230321, 0.5499500917132223], [0.5088975485716484, 0.44700099111466074], [-0.7307687194348117, -0.7203582562630154], [1.622968630082777, 1.6859886115633318], [0.30262090327049823, 0.2882553730460223]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [21.02220744409841, 28.889462439852874], "std": [8.707706178768547, 11.553396813667037], "constant": [false, false]}} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/1/predictions.npz b/benchmarks/v1/evidence/multi-worker-045/A6/1/predictions.npz new file mode 100644 index 0000000..8bfb873 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/1/predictions.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/1/replay.npz b/benchmarks/v1/evidence/multi-worker-045/A6/1/replay.npz new file mode 100644 index 0000000..8bfb873 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/1/replay.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/1/training.json b/benchmarks/v1/evidence/multi-worker-045/A6/1/training.json new file mode 100644 index 0000000..1626f8f --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/1/training.json @@ -0,0 +1,33 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.8580941834471312, + "last_score": 0.8921598747373427, + "completed_rounds": 4, + "stale_rounds": 3, + "reason": "patience" + }, + "accepted_commits": 4, + "selected_model_identity": "dd8151781709409be307bfbf17c1cd006c4d358b281e5ea80c49d0be970b78a5", + "best_validation_score": 0.8580941834471312, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/1/worker.log b/benchmarks/v1/evidence/multi-worker-045/A6/1/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/2/execution.json b/benchmarks/v1/evidence/multi-worker-045/A6/2/execution.json new file mode 100644 index 0000000..4dfa459 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/2/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "e4cd1bdce695b2ff56d99408b02176f39b2be6e74e848e68bf5f21bbd5d2539d", + "predictions.npz": "34baef0cb200fa5767c59c967247f74efa75b79ed8ca62e768b7c9e41d437b6a", + "training.json": "5d7920c7094fde7c69674c003a4f231bb58e3a7249818ab6e08c6acf1a37612a", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/2/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 2.0311060410022037 +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/2/model.bin b/benchmarks/v1/evidence/multi-worker-045/A6/2/model.bin new file mode 100644 index 0000000..e08a8a1 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/2/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "base": [1.2523714162070154e-14, 9.602475403908996e-15], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 57.0, 58.0, 59.0, 60.0, 61.0, 63.0, 65.0, 66.0, 67.0, 68.0, 71.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.6042, 13.422, 19.43253125, 24.315, 29.59721875, 35.306, 41.30290625, 46.775, 51.42059375, 56.399, 62.345187499999994, 68.333, 73.33206249999999, 78.70281250000001, 84.5803125, 91.755, 98.7426875, 105.5, 112.28, 118.3, 123.83, 128.36, 133.9871875, 140.29, 145.8, 150.48, 156.39, 161.48625, 167.87, 174.39, 182.53625], [0.0021884375000000003, 0.0025, 0.0027253125, 0.00293, 0.00314, 0.00332, 0.0034690625, 0.00363, 0.00381, 0.00396, 0.00411, 0.00424, 0.0043996875, 0.00454, 0.00471, 0.004875, 0.0050434375, 0.00521, 0.0053703125, 0.00553, 0.0057471875, 0.00594, 0.00617, 0.00644, 0.0067209375, 0.00704, 0.00744, 0.00796625, 0.0087, 0.00982625, 0.012763125], [1.1666875e-05, 1.356e-05, 1.571e-05, 1.741e-05, 1.877875e-05, 2.0260625e-05, 2.1488125e-05, 2.30575e-05, 2.442e-05, 2.5694375e-05, 2.70728125e-05, 2.836125e-05, 2.9839687499999997e-05, 3.111625e-05, 3.226e-05, 3.372e-05, 3.537e-05, 3.723e-05, 3.8610312499999996e-05, 4.034e-05, 4.222e-05, 4.464125e-05, 4.674e-05, 4.932e-05, 5.24228125e-05, 5.5428750000000004e-05, 5.91878125e-05, 6.34625e-05, 7.031375e-05, 8.194625000000001e-05, 0.00010109625], [0.0009, 0.001076875, 0.00119, 0.00126, 0.0013521875, 0.00144, 0.00152, 0.00159, 0.00168, 0.00176, 0.00182, 0.0019, 0.00198, 0.00206, 0.00213, 0.00222, 0.00231, 0.00241, 0.0025103125, 0.00262, 0.00274, 0.00287, 0.0029840625, 0.00314, 0.00331, 0.0035, 0.00377, 0.00405, 0.00441, 0.00518, 0.0068415624999999996], [0.00107, 0.00126, 0.00139, 0.00151, 0.0016, 0.00168, 0.00176, 0.00185, 0.00193, 0.002, 0.00208, 0.00215, 0.0022196875000000003, 0.00229, 0.00237, 0.00246, 0.00254, 0.00264, 0.00275, 0.00284, 0.00293, 0.00304, 0.00315, 0.00328, 0.0034, 0.00357, 0.00381, 0.00414, 0.00453, 0.005213125, 0.006739375], [0.00271, 0.0032268749999999997, 0.00356, 0.00378375, 0.0040621875, 0.00431, 0.00456, 0.00478, 0.00504, 0.00527, 0.00546, 0.0057, 0.00595, 0.00618, 0.0064, 0.00667, 0.0069234375, 0.00723, 0.0075403125, 0.007858750000000001, 0.00822, 0.0086, 0.0089540625, 0.00941, 0.00992, 0.0105, 0.0113078125, 0.01216, 0.01323, 0.01554, 0.0205315625], [0.01158, 0.01349, 0.0151053125, 0.01638, 0.01739, 0.01815, 0.01918, 0.0202, 0.021201875, 0.02212875, 0.02306, 0.024001250000000002, 0.02476, 0.0259, 0.026896562500000002, 0.02788, 0.02883, 0.03006, 0.0314003125, 0.03254625, 0.03401, 0.035375625, 0.0369240625, 0.038904999999999995, 0.04120375, 0.043837499999999995, 0.046745625, 0.04972625, 0.0538640625, 0.060391875, 0.0726846875], [0.106, 0.1226875, 0.137, 0.148, 0.157, 0.165, 0.17490624999999999, 0.183, 0.192, 0.201, 0.21, 0.218, 0.226, 0.236, 0.245, 0.255, 0.265, 0.276, 0.288, 0.299, 0.31, 0.327, 0.341, 0.359, 0.37409375, 0.4, 0.426, 0.459, 0.49846875, 0.551, 0.659], [0.0051984375, 0.00626, 0.0069753125, 0.007677499999999999, 0.0083, 0.00879, 0.00938, 0.00984, 0.01032, 0.01079, 0.01128, 0.01177, 0.0122496875, 0.012738125, 0.0133065625, 0.013835, 0.0143934375, 0.01501, 0.0156703125, 0.016368749999999998, 0.017067187499999997, 0.017975625, 0.01903625, 0.020045, 0.021400937499999998, 0.023008749999999998, 0.02449, 0.0262825, 0.0286846875, 0.032433125, 0.039119375], [0.0063384375, 0.00747, 0.0083853125, 0.00924375, 0.00978, 0.010300625, 0.0108390625, 0.011457499999999999, 0.01195, 0.012548750000000001, 0.01319, 0.01373, 0.0142996875, 0.01486, 0.01547, 0.01614, 0.016783437499999998, 0.01757, 0.01822, 0.0189, 0.01982, 0.020965624999999998, 0.021958125000000002, 0.0230925, 0.02442, 0.02614, 0.0280134375, 0.03002625, 0.0323940625, 0.036173125, 0.0431225], [0.0093, 0.011056875, 0.0124059375, 0.0133, 0.01413, 0.015000625, 0.015809062500000002, 0.01661, 0.0174, 0.01824, 0.0190628125, 0.0198325, 0.02064, 0.02154, 0.02227, 0.02306, 0.024026875, 0.024991875, 0.02596, 0.0270075, 0.0280671875, 0.029345625, 0.03066625, 0.03219, 0.0334709375, 0.03519875, 0.037235625, 0.03997625, 0.0438146875, 0.04849875, 0.0579371875], [0.015605312500000001, 0.018776875000000002, 0.0209265625, 0.023035, 0.0249021875, 0.02636, 0.02813, 0.02952, 0.03097, 0.03238, 0.033835625, 0.03531, 0.0367396875, 0.038224375, 0.039929687500000005, 0.041514999999999996, 0.043176874999999997, 0.04502, 0.047020624999999996, 0.04909875, 0.0512115625, 0.053916875, 0.05711875, 0.060125, 0.06420374999999999, 0.06902625, 0.0734678125, 0.07884125, 0.0860640625, 0.097289375, 0.1173496875], [0.00358784375, 0.005151, 0.0064490625, 0.0077585, 0.00864109375, 0.0094610625, 0.0105618125, 0.0116645, 0.012465, 0.013374125, 0.01428928125, 0.015268625000000001, 0.01631571875, 0.0174278125, 0.0184175625, 0.019416500000000003, 0.02071921875, 0.022056625, 0.0234273125, 0.02486625, 0.026339, 0.02814, 0.02995503125, 0.031855, 0.034380125, 0.036633875, 0.03997046875, 0.04484775, 0.05002075, 0.05986325, 0.088562875], [15.014125, 16.159625, 17.11178125, 17.740375, 18.24528125, 18.746, 19.082, 19.44225, 19.7861875, 20.08975, 20.341843750000002, 20.65425, 20.9499375, 21.2148125, 21.493, 21.7505, 21.9996875, 22.2936875, 22.5880625, 22.82675, 23.17603125, 23.448, 23.783, 24.125, 24.472093750000003, 24.78, 25.197468750000002, 25.586, 26.15915625, 26.990312499999998, 28.09303125], [0.3754990625, 0.4031725, 0.418465, 0.43200500000000003, 0.4442540625, 0.457193125, 0.46804124999999996, 0.4782925, 0.48827375, 0.497398125, 0.5057034375, 0.51403625, 0.5220484375, 0.53118875, 0.539963125, 0.548125, 0.5558793750000001, 0.5648825000000001, 0.57218625, 0.58051125, 0.5904596875, 0.59986125, 0.607184375, 0.6145125, 0.6234909375, 0.6324762500000001, 0.64416875, 0.65400625, 0.6660165625, 0.679333125, 0.7032109375000001], [0.54623375, 0.555919375, 0.5635971875, 0.570365, 0.57583, 0.5807106249999999, 0.5861853124999999, 0.5913375, 0.5962359375, 0.600533125, 0.6051928125, 0.6101325, 0.6155878124999999, 0.62106625, 0.62632625, 0.633135, 0.6383203125, 0.643709375, 0.64998125, 0.65658375, 0.6651818749999999, 0.6735575, 0.68189625, 0.692355, 0.7001556250000001, 0.708359375, 0.7164746875, 0.7247925, 0.7332546875, 0.74243875, 0.7560178125], [0.0923604375, 0.10662, 0.1191825, 0.1295, 0.13764625, 0.1441525, 0.1501034375, 0.156215, 0.163111875, 0.16860875, 0.173968125, 0.180555, 0.185983125, 0.191398125, 0.19736843750000002, 0.203745, 0.2096403125, 0.21633375, 0.2221034375, 0.22884875, 0.23540375, 0.24266875, 0.24900031250000002, 0.25615, 0.26354, 0.27270625, 0.282846875, 0.296715, 0.3123215625, 0.33389875, 0.379433125], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 17, -1, -1, -1, -1], "threshold": [7, 6, 23, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-2.429393372952057e-16, -3.5338711076452543e-15], [-0.5062214779879735, -0.44674738169708944], [0.34314967191729534, 0.302834281288451], [-0.2773816719572907, -0.2670264715698439], [-1.471720865614163, -1.2048345359168815], [0.1218072830451359, 0.08188769520402536], [1.2532312918360984, 1.2113819160658013]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 57.0, 58.0, 59.0, 60.0, 61.0, 63.0, 65.0, 66.0, 67.0, 68.0, 71.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.6042, 13.422, 19.43253125, 24.315, 29.59721875, 35.306, 41.30290625, 46.775, 51.42059375, 56.399, 62.345187499999994, 68.333, 73.33206249999999, 78.70281250000001, 84.5803125, 91.755, 98.7426875, 105.5, 112.28, 118.3, 123.83, 128.36, 133.9871875, 140.29, 145.8, 150.48, 156.39, 161.48625, 167.87, 174.39, 182.53625], [0.0021884375000000003, 0.0025, 0.0027253125, 0.00293, 0.00314, 0.00332, 0.0034690625, 0.00363, 0.00381, 0.00396, 0.00411, 0.00424, 0.0043996875, 0.00454, 0.00471, 0.004875, 0.0050434375, 0.00521, 0.0053703125, 0.00553, 0.0057471875, 0.00594, 0.00617, 0.00644, 0.0067209375, 0.00704, 0.00744, 0.00796625, 0.0087, 0.00982625, 0.012763125], [1.1666875e-05, 1.356e-05, 1.571e-05, 1.741e-05, 1.877875e-05, 2.0260625e-05, 2.1488125e-05, 2.30575e-05, 2.442e-05, 2.5694375e-05, 2.70728125e-05, 2.836125e-05, 2.9839687499999997e-05, 3.111625e-05, 3.226e-05, 3.372e-05, 3.537e-05, 3.723e-05, 3.8610312499999996e-05, 4.034e-05, 4.222e-05, 4.464125e-05, 4.674e-05, 4.932e-05, 5.24228125e-05, 5.5428750000000004e-05, 5.91878125e-05, 6.34625e-05, 7.031375e-05, 8.194625000000001e-05, 0.00010109625], [0.0009, 0.001076875, 0.00119, 0.00126, 0.0013521875, 0.00144, 0.00152, 0.00159, 0.00168, 0.00176, 0.00182, 0.0019, 0.00198, 0.00206, 0.00213, 0.00222, 0.00231, 0.00241, 0.0025103125, 0.00262, 0.00274, 0.00287, 0.0029840625, 0.00314, 0.00331, 0.0035, 0.00377, 0.00405, 0.00441, 0.00518, 0.0068415624999999996], [0.00107, 0.00126, 0.00139, 0.00151, 0.0016, 0.00168, 0.00176, 0.00185, 0.00193, 0.002, 0.00208, 0.00215, 0.0022196875000000003, 0.00229, 0.00237, 0.00246, 0.00254, 0.00264, 0.00275, 0.00284, 0.00293, 0.00304, 0.00315, 0.00328, 0.0034, 0.00357, 0.00381, 0.00414, 0.00453, 0.005213125, 0.006739375], [0.00271, 0.0032268749999999997, 0.00356, 0.00378375, 0.0040621875, 0.00431, 0.00456, 0.00478, 0.00504, 0.00527, 0.00546, 0.0057, 0.00595, 0.00618, 0.0064, 0.00667, 0.0069234375, 0.00723, 0.0075403125, 0.007858750000000001, 0.00822, 0.0086, 0.0089540625, 0.00941, 0.00992, 0.0105, 0.0113078125, 0.01216, 0.01323, 0.01554, 0.0205315625], [0.01158, 0.01349, 0.0151053125, 0.01638, 0.01739, 0.01815, 0.01918, 0.0202, 0.021201875, 0.02212875, 0.02306, 0.024001250000000002, 0.02476, 0.0259, 0.026896562500000002, 0.02788, 0.02883, 0.03006, 0.0314003125, 0.03254625, 0.03401, 0.035375625, 0.0369240625, 0.038904999999999995, 0.04120375, 0.043837499999999995, 0.046745625, 0.04972625, 0.0538640625, 0.060391875, 0.0726846875], [0.106, 0.1226875, 0.137, 0.148, 0.157, 0.165, 0.17490624999999999, 0.183, 0.192, 0.201, 0.21, 0.218, 0.226, 0.236, 0.245, 0.255, 0.265, 0.276, 0.288, 0.299, 0.31, 0.327, 0.341, 0.359, 0.37409375, 0.4, 0.426, 0.459, 0.49846875, 0.551, 0.659], [0.0051984375, 0.00626, 0.0069753125, 0.007677499999999999, 0.0083, 0.00879, 0.00938, 0.00984, 0.01032, 0.01079, 0.01128, 0.01177, 0.0122496875, 0.012738125, 0.0133065625, 0.013835, 0.0143934375, 0.01501, 0.0156703125, 0.016368749999999998, 0.017067187499999997, 0.017975625, 0.01903625, 0.020045, 0.021400937499999998, 0.023008749999999998, 0.02449, 0.0262825, 0.0286846875, 0.032433125, 0.039119375], [0.0063384375, 0.00747, 0.0083853125, 0.00924375, 0.00978, 0.010300625, 0.0108390625, 0.011457499999999999, 0.01195, 0.012548750000000001, 0.01319, 0.01373, 0.0142996875, 0.01486, 0.01547, 0.01614, 0.016783437499999998, 0.01757, 0.01822, 0.0189, 0.01982, 0.020965624999999998, 0.021958125000000002, 0.0230925, 0.02442, 0.02614, 0.0280134375, 0.03002625, 0.0323940625, 0.036173125, 0.0431225], [0.0093, 0.011056875, 0.0124059375, 0.0133, 0.01413, 0.015000625, 0.015809062500000002, 0.01661, 0.0174, 0.01824, 0.0190628125, 0.0198325, 0.02064, 0.02154, 0.02227, 0.02306, 0.024026875, 0.024991875, 0.02596, 0.0270075, 0.0280671875, 0.029345625, 0.03066625, 0.03219, 0.0334709375, 0.03519875, 0.037235625, 0.03997625, 0.0438146875, 0.04849875, 0.0579371875], [0.015605312500000001, 0.018776875000000002, 0.0209265625, 0.023035, 0.0249021875, 0.02636, 0.02813, 0.02952, 0.03097, 0.03238, 0.033835625, 0.03531, 0.0367396875, 0.038224375, 0.039929687500000005, 0.041514999999999996, 0.043176874999999997, 0.04502, 0.047020624999999996, 0.04909875, 0.0512115625, 0.053916875, 0.05711875, 0.060125, 0.06420374999999999, 0.06902625, 0.0734678125, 0.07884125, 0.0860640625, 0.097289375, 0.1173496875], [0.00358784375, 0.005151, 0.0064490625, 0.0077585, 0.00864109375, 0.0094610625, 0.0105618125, 0.0116645, 0.012465, 0.013374125, 0.01428928125, 0.015268625000000001, 0.01631571875, 0.0174278125, 0.0184175625, 0.019416500000000003, 0.02071921875, 0.022056625, 0.0234273125, 0.02486625, 0.026339, 0.02814, 0.02995503125, 0.031855, 0.034380125, 0.036633875, 0.03997046875, 0.04484775, 0.05002075, 0.05986325, 0.088562875], [15.014125, 16.159625, 17.11178125, 17.740375, 18.24528125, 18.746, 19.082, 19.44225, 19.7861875, 20.08975, 20.341843750000002, 20.65425, 20.9499375, 21.2148125, 21.493, 21.7505, 21.9996875, 22.2936875, 22.5880625, 22.82675, 23.17603125, 23.448, 23.783, 24.125, 24.472093750000003, 24.78, 25.197468750000002, 25.586, 26.15915625, 26.990312499999998, 28.09303125], [0.3754990625, 0.4031725, 0.418465, 0.43200500000000003, 0.4442540625, 0.457193125, 0.46804124999999996, 0.4782925, 0.48827375, 0.497398125, 0.5057034375, 0.51403625, 0.5220484375, 0.53118875, 0.539963125, 0.548125, 0.5558793750000001, 0.5648825000000001, 0.57218625, 0.58051125, 0.5904596875, 0.59986125, 0.607184375, 0.6145125, 0.6234909375, 0.6324762500000001, 0.64416875, 0.65400625, 0.6660165625, 0.679333125, 0.7032109375000001], [0.54623375, 0.555919375, 0.5635971875, 0.570365, 0.57583, 0.5807106249999999, 0.5861853124999999, 0.5913375, 0.5962359375, 0.600533125, 0.6051928125, 0.6101325, 0.6155878124999999, 0.62106625, 0.62632625, 0.633135, 0.6383203125, 0.643709375, 0.64998125, 0.65658375, 0.6651818749999999, 0.6735575, 0.68189625, 0.692355, 0.7001556250000001, 0.708359375, 0.7164746875, 0.7247925, 0.7332546875, 0.74243875, 0.7560178125], [0.0923604375, 0.10662, 0.1191825, 0.1295, 0.13764625, 0.1441525, 0.1501034375, 0.156215, 0.163111875, 0.16860875, 0.173968125, 0.180555, 0.185983125, 0.191398125, 0.19736843750000002, 0.203745, 0.2096403125, 0.21633375, 0.2221034375, 0.22884875, 0.23540375, 0.24266875, 0.24900031250000002, 0.25615, 0.26354, 0.27270625, 0.282846875, 0.296715, 0.3123215625, 0.33389875, 0.379433125], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [7, 6, 11, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.0262385807531357e-05, -4.899626785981901e-06], [-0.45571807442118983, -0.40217256620270253], [0.308897983076919, 0.2726103685581919], [-0.249666775036055, -0.24034622596248684], [-1.3250706658845226, -1.0847783286145254], [0.850453636541522, 0.6863699915780971], [-0.07537253375338605, -0.021008963464223495]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 57.0, 58.0, 59.0, 60.0, 61.0, 63.0, 65.0, 66.0, 67.0, 68.0, 71.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.6042, 13.422, 19.43253125, 24.315, 29.59721875, 35.306, 41.30290625, 46.775, 51.42059375, 56.399, 62.345187499999994, 68.333, 73.33206249999999, 78.70281250000001, 84.5803125, 91.755, 98.7426875, 105.5, 112.28, 118.3, 123.83, 128.36, 133.9871875, 140.29, 145.8, 150.48, 156.39, 161.48625, 167.87, 174.39, 182.53625], [0.0021884375000000003, 0.0025, 0.0027253125, 0.00293, 0.00314, 0.00332, 0.0034690625, 0.00363, 0.00381, 0.00396, 0.00411, 0.00424, 0.0043996875, 0.00454, 0.00471, 0.004875, 0.0050434375, 0.00521, 0.0053703125, 0.00553, 0.0057471875, 0.00594, 0.00617, 0.00644, 0.0067209375, 0.00704, 0.00744, 0.00796625, 0.0087, 0.00982625, 0.012763125], [1.1666875e-05, 1.356e-05, 1.571e-05, 1.741e-05, 1.877875e-05, 2.0260625e-05, 2.1488125e-05, 2.30575e-05, 2.442e-05, 2.5694375e-05, 2.70728125e-05, 2.836125e-05, 2.9839687499999997e-05, 3.111625e-05, 3.226e-05, 3.372e-05, 3.537e-05, 3.723e-05, 3.8610312499999996e-05, 4.034e-05, 4.222e-05, 4.464125e-05, 4.674e-05, 4.932e-05, 5.24228125e-05, 5.5428750000000004e-05, 5.91878125e-05, 6.34625e-05, 7.031375e-05, 8.194625000000001e-05, 0.00010109625], [0.0009, 0.001076875, 0.00119, 0.00126, 0.0013521875, 0.00144, 0.00152, 0.00159, 0.00168, 0.00176, 0.00182, 0.0019, 0.00198, 0.00206, 0.00213, 0.00222, 0.00231, 0.00241, 0.0025103125, 0.00262, 0.00274, 0.00287, 0.0029840625, 0.00314, 0.00331, 0.0035, 0.00377, 0.00405, 0.00441, 0.00518, 0.0068415624999999996], [0.00107, 0.00126, 0.00139, 0.00151, 0.0016, 0.00168, 0.00176, 0.00185, 0.00193, 0.002, 0.00208, 0.00215, 0.0022196875000000003, 0.00229, 0.00237, 0.00246, 0.00254, 0.00264, 0.00275, 0.00284, 0.00293, 0.00304, 0.00315, 0.00328, 0.0034, 0.00357, 0.00381, 0.00414, 0.00453, 0.005213125, 0.006739375], [0.00271, 0.0032268749999999997, 0.00356, 0.00378375, 0.0040621875, 0.00431, 0.00456, 0.00478, 0.00504, 0.00527, 0.00546, 0.0057, 0.00595, 0.00618, 0.0064, 0.00667, 0.0069234375, 0.00723, 0.0075403125, 0.007858750000000001, 0.00822, 0.0086, 0.0089540625, 0.00941, 0.00992, 0.0105, 0.0113078125, 0.01216, 0.01323, 0.01554, 0.0205315625], [0.01158, 0.01349, 0.0151053125, 0.01638, 0.01739, 0.01815, 0.01918, 0.0202, 0.021201875, 0.02212875, 0.02306, 0.024001250000000002, 0.02476, 0.0259, 0.026896562500000002, 0.02788, 0.02883, 0.03006, 0.0314003125, 0.03254625, 0.03401, 0.035375625, 0.0369240625, 0.038904999999999995, 0.04120375, 0.043837499999999995, 0.046745625, 0.04972625, 0.0538640625, 0.060391875, 0.0726846875], [0.106, 0.1226875, 0.137, 0.148, 0.157, 0.165, 0.17490624999999999, 0.183, 0.192, 0.201, 0.21, 0.218, 0.226, 0.236, 0.245, 0.255, 0.265, 0.276, 0.288, 0.299, 0.31, 0.327, 0.341, 0.359, 0.37409375, 0.4, 0.426, 0.459, 0.49846875, 0.551, 0.659], [0.0051984375, 0.00626, 0.0069753125, 0.007677499999999999, 0.0083, 0.00879, 0.00938, 0.00984, 0.01032, 0.01079, 0.01128, 0.01177, 0.0122496875, 0.012738125, 0.0133065625, 0.013835, 0.0143934375, 0.01501, 0.0156703125, 0.016368749999999998, 0.017067187499999997, 0.017975625, 0.01903625, 0.020045, 0.021400937499999998, 0.023008749999999998, 0.02449, 0.0262825, 0.0286846875, 0.032433125, 0.039119375], [0.0063384375, 0.00747, 0.0083853125, 0.00924375, 0.00978, 0.010300625, 0.0108390625, 0.011457499999999999, 0.01195, 0.012548750000000001, 0.01319, 0.01373, 0.0142996875, 0.01486, 0.01547, 0.01614, 0.016783437499999998, 0.01757, 0.01822, 0.0189, 0.01982, 0.020965624999999998, 0.021958125000000002, 0.0230925, 0.02442, 0.02614, 0.0280134375, 0.03002625, 0.0323940625, 0.036173125, 0.0431225], [0.0093, 0.011056875, 0.0124059375, 0.0133, 0.01413, 0.015000625, 0.015809062500000002, 0.01661, 0.0174, 0.01824, 0.0190628125, 0.0198325, 0.02064, 0.02154, 0.02227, 0.02306, 0.024026875, 0.024991875, 0.02596, 0.0270075, 0.0280671875, 0.029345625, 0.03066625, 0.03219, 0.0334709375, 0.03519875, 0.037235625, 0.03997625, 0.0438146875, 0.04849875, 0.0579371875], [0.015605312500000001, 0.018776875000000002, 0.0209265625, 0.023035, 0.0249021875, 0.02636, 0.02813, 0.02952, 0.03097, 0.03238, 0.033835625, 0.03531, 0.0367396875, 0.038224375, 0.039929687500000005, 0.041514999999999996, 0.043176874999999997, 0.04502, 0.047020624999999996, 0.04909875, 0.0512115625, 0.053916875, 0.05711875, 0.060125, 0.06420374999999999, 0.06902625, 0.0734678125, 0.07884125, 0.0860640625, 0.097289375, 0.1173496875], [0.00358784375, 0.005151, 0.0064490625, 0.0077585, 0.00864109375, 0.0094610625, 0.0105618125, 0.0116645, 0.012465, 0.013374125, 0.01428928125, 0.015268625000000001, 0.01631571875, 0.0174278125, 0.0184175625, 0.019416500000000003, 0.02071921875, 0.022056625, 0.0234273125, 0.02486625, 0.026339, 0.02814, 0.02995503125, 0.031855, 0.034380125, 0.036633875, 0.03997046875, 0.04484775, 0.05002075, 0.05986325, 0.088562875], [15.014125, 16.159625, 17.11178125, 17.740375, 18.24528125, 18.746, 19.082, 19.44225, 19.7861875, 20.08975, 20.341843750000002, 20.65425, 20.9499375, 21.2148125, 21.493, 21.7505, 21.9996875, 22.2936875, 22.5880625, 22.82675, 23.17603125, 23.448, 23.783, 24.125, 24.472093750000003, 24.78, 25.197468750000002, 25.586, 26.15915625, 26.990312499999998, 28.09303125], [0.3754990625, 0.4031725, 0.418465, 0.43200500000000003, 0.4442540625, 0.457193125, 0.46804124999999996, 0.4782925, 0.48827375, 0.497398125, 0.5057034375, 0.51403625, 0.5220484375, 0.53118875, 0.539963125, 0.548125, 0.5558793750000001, 0.5648825000000001, 0.57218625, 0.58051125, 0.5904596875, 0.59986125, 0.607184375, 0.6145125, 0.6234909375, 0.6324762500000001, 0.64416875, 0.65400625, 0.6660165625, 0.679333125, 0.7032109375000001], [0.54623375, 0.555919375, 0.5635971875, 0.570365, 0.57583, 0.5807106249999999, 0.5861853124999999, 0.5913375, 0.5962359375, 0.600533125, 0.6051928125, 0.6101325, 0.6155878124999999, 0.62106625, 0.62632625, 0.633135, 0.6383203125, 0.643709375, 0.64998125, 0.65658375, 0.6651818749999999, 0.6735575, 0.68189625, 0.692355, 0.7001556250000001, 0.708359375, 0.7164746875, 0.7247925, 0.7332546875, 0.74243875, 0.7560178125], [0.0923604375, 0.10662, 0.1191825, 0.1295, 0.13764625, 0.1441525, 0.1501034375, 0.156215, 0.163111875, 0.16860875, 0.173968125, 0.180555, 0.185983125, 0.191398125, 0.19736843750000002, 0.203745, 0.2096403125, 0.21633375, 0.2221034375, 0.22884875, 0.23540375, 0.24266875, 0.24900031250000002, 0.25615, 0.26354, 0.27270625, 0.282846875, 0.296715, 0.3123215625, 0.33389875, 0.379433125], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 17, -1, -1, -1, -1], "threshold": [7, 6, 22, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-3.1174592716038254e-05, -2.2510172375874025e-05], [-0.4102531737978711, -0.3620452705162119], [0.27804385348081406, 0.2453799511698431], [-0.22472104273169735, -0.21633176664022857], [-1.1930334825109237, -0.9766851689192485], [0.06861978675051957, 0.0369927336070516], [1.0346704814812608, 0.9983269820791089]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[55.0, 57.0, 58.0, 59.0, 60.0, 61.0, 63.0, 65.0, 66.0, 67.0, 68.0, 71.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.6042, 13.422, 19.43253125, 24.315, 29.59721875, 35.306, 41.30290625, 46.775, 51.42059375, 56.399, 62.345187499999994, 68.333, 73.33206249999999, 78.70281250000001, 84.5803125, 91.755, 98.7426875, 105.5, 112.28, 118.3, 123.83, 128.36, 133.9871875, 140.29, 145.8, 150.48, 156.39, 161.48625, 167.87, 174.39, 182.53625], [0.0021884375000000003, 0.0025, 0.0027253125, 0.00293, 0.00314, 0.00332, 0.0034690625, 0.00363, 0.00381, 0.00396, 0.00411, 0.00424, 0.0043996875, 0.00454, 0.00471, 0.004875, 0.0050434375, 0.00521, 0.0053703125, 0.00553, 0.0057471875, 0.00594, 0.00617, 0.00644, 0.0067209375, 0.00704, 0.00744, 0.00796625, 0.0087, 0.00982625, 0.012763125], [1.1666875e-05, 1.356e-05, 1.571e-05, 1.741e-05, 1.877875e-05, 2.0260625e-05, 2.1488125e-05, 2.30575e-05, 2.442e-05, 2.5694375e-05, 2.70728125e-05, 2.836125e-05, 2.9839687499999997e-05, 3.111625e-05, 3.226e-05, 3.372e-05, 3.537e-05, 3.723e-05, 3.8610312499999996e-05, 4.034e-05, 4.222e-05, 4.464125e-05, 4.674e-05, 4.932e-05, 5.24228125e-05, 5.5428750000000004e-05, 5.91878125e-05, 6.34625e-05, 7.031375e-05, 8.194625000000001e-05, 0.00010109625], [0.0009, 0.001076875, 0.00119, 0.00126, 0.0013521875, 0.00144, 0.00152, 0.00159, 0.00168, 0.00176, 0.00182, 0.0019, 0.00198, 0.00206, 0.00213, 0.00222, 0.00231, 0.00241, 0.0025103125, 0.00262, 0.00274, 0.00287, 0.0029840625, 0.00314, 0.00331, 0.0035, 0.00377, 0.00405, 0.00441, 0.00518, 0.0068415624999999996], [0.00107, 0.00126, 0.00139, 0.00151, 0.0016, 0.00168, 0.00176, 0.00185, 0.00193, 0.002, 0.00208, 0.00215, 0.0022196875000000003, 0.00229, 0.00237, 0.00246, 0.00254, 0.00264, 0.00275, 0.00284, 0.00293, 0.00304, 0.00315, 0.00328, 0.0034, 0.00357, 0.00381, 0.00414, 0.00453, 0.005213125, 0.006739375], [0.00271, 0.0032268749999999997, 0.00356, 0.00378375, 0.0040621875, 0.00431, 0.00456, 0.00478, 0.00504, 0.00527, 0.00546, 0.0057, 0.00595, 0.00618, 0.0064, 0.00667, 0.0069234375, 0.00723, 0.0075403125, 0.007858750000000001, 0.00822, 0.0086, 0.0089540625, 0.00941, 0.00992, 0.0105, 0.0113078125, 0.01216, 0.01323, 0.01554, 0.0205315625], [0.01158, 0.01349, 0.0151053125, 0.01638, 0.01739, 0.01815, 0.01918, 0.0202, 0.021201875, 0.02212875, 0.02306, 0.024001250000000002, 0.02476, 0.0259, 0.026896562500000002, 0.02788, 0.02883, 0.03006, 0.0314003125, 0.03254625, 0.03401, 0.035375625, 0.0369240625, 0.038904999999999995, 0.04120375, 0.043837499999999995, 0.046745625, 0.04972625, 0.0538640625, 0.060391875, 0.0726846875], [0.106, 0.1226875, 0.137, 0.148, 0.157, 0.165, 0.17490624999999999, 0.183, 0.192, 0.201, 0.21, 0.218, 0.226, 0.236, 0.245, 0.255, 0.265, 0.276, 0.288, 0.299, 0.31, 0.327, 0.341, 0.359, 0.37409375, 0.4, 0.426, 0.459, 0.49846875, 0.551, 0.659], [0.0051984375, 0.00626, 0.0069753125, 0.007677499999999999, 0.0083, 0.00879, 0.00938, 0.00984, 0.01032, 0.01079, 0.01128, 0.01177, 0.0122496875, 0.012738125, 0.0133065625, 0.013835, 0.0143934375, 0.01501, 0.0156703125, 0.016368749999999998, 0.017067187499999997, 0.017975625, 0.01903625, 0.020045, 0.021400937499999998, 0.023008749999999998, 0.02449, 0.0262825, 0.0286846875, 0.032433125, 0.039119375], [0.0063384375, 0.00747, 0.0083853125, 0.00924375, 0.00978, 0.010300625, 0.0108390625, 0.011457499999999999, 0.01195, 0.012548750000000001, 0.01319, 0.01373, 0.0142996875, 0.01486, 0.01547, 0.01614, 0.016783437499999998, 0.01757, 0.01822, 0.0189, 0.01982, 0.020965624999999998, 0.021958125000000002, 0.0230925, 0.02442, 0.02614, 0.0280134375, 0.03002625, 0.0323940625, 0.036173125, 0.0431225], [0.0093, 0.011056875, 0.0124059375, 0.0133, 0.01413, 0.015000625, 0.015809062500000002, 0.01661, 0.0174, 0.01824, 0.0190628125, 0.0198325, 0.02064, 0.02154, 0.02227, 0.02306, 0.024026875, 0.024991875, 0.02596, 0.0270075, 0.0280671875, 0.029345625, 0.03066625, 0.03219, 0.0334709375, 0.03519875, 0.037235625, 0.03997625, 0.0438146875, 0.04849875, 0.0579371875], [0.015605312500000001, 0.018776875000000002, 0.0209265625, 0.023035, 0.0249021875, 0.02636, 0.02813, 0.02952, 0.03097, 0.03238, 0.033835625, 0.03531, 0.0367396875, 0.038224375, 0.039929687500000005, 0.041514999999999996, 0.043176874999999997, 0.04502, 0.047020624999999996, 0.04909875, 0.0512115625, 0.053916875, 0.05711875, 0.060125, 0.06420374999999999, 0.06902625, 0.0734678125, 0.07884125, 0.0860640625, 0.097289375, 0.1173496875], [0.00358784375, 0.005151, 0.0064490625, 0.0077585, 0.00864109375, 0.0094610625, 0.0105618125, 0.0116645, 0.012465, 0.013374125, 0.01428928125, 0.015268625000000001, 0.01631571875, 0.0174278125, 0.0184175625, 0.019416500000000003, 0.02071921875, 0.022056625, 0.0234273125, 0.02486625, 0.026339, 0.02814, 0.02995503125, 0.031855, 0.034380125, 0.036633875, 0.03997046875, 0.04484775, 0.05002075, 0.05986325, 0.088562875], [15.014125, 16.159625, 17.11178125, 17.740375, 18.24528125, 18.746, 19.082, 19.44225, 19.7861875, 20.08975, 20.341843750000002, 20.65425, 20.9499375, 21.2148125, 21.493, 21.7505, 21.9996875, 22.2936875, 22.5880625, 22.82675, 23.17603125, 23.448, 23.783, 24.125, 24.472093750000003, 24.78, 25.197468750000002, 25.586, 26.15915625, 26.990312499999998, 28.09303125], [0.3754990625, 0.4031725, 0.418465, 0.43200500000000003, 0.4442540625, 0.457193125, 0.46804124999999996, 0.4782925, 0.48827375, 0.497398125, 0.5057034375, 0.51403625, 0.5220484375, 0.53118875, 0.539963125, 0.548125, 0.5558793750000001, 0.5648825000000001, 0.57218625, 0.58051125, 0.5904596875, 0.59986125, 0.607184375, 0.6145125, 0.6234909375, 0.6324762500000001, 0.64416875, 0.65400625, 0.6660165625, 0.679333125, 0.7032109375000001], [0.54623375, 0.555919375, 0.5635971875, 0.570365, 0.57583, 0.5807106249999999, 0.5861853124999999, 0.5913375, 0.5962359375, 0.600533125, 0.6051928125, 0.6101325, 0.6155878124999999, 0.62106625, 0.62632625, 0.633135, 0.6383203125, 0.643709375, 0.64998125, 0.65658375, 0.6651818749999999, 0.6735575, 0.68189625, 0.692355, 0.7001556250000001, 0.708359375, 0.7164746875, 0.7247925, 0.7332546875, 0.74243875, 0.7560178125], [0.0923604375, 0.10662, 0.1191825, 0.1295, 0.13764625, 0.1441525, 0.1501034375, 0.156215, 0.163111875, 0.16860875, 0.173968125, 0.180555, 0.185983125, 0.191398125, 0.19736843750000002, 0.203745, 0.2096403125, 0.21633375, 0.2221034375, 0.22884875, 0.23540375, 0.24266875, 0.24900031250000002, 0.25615, 0.26354, 0.27270625, 0.282846875, 0.296715, 0.3123215625, 0.33389875, 0.379433125], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 17, -1, -1, -1, -1], "threshold": [7, 6, 23, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-3.668441073056017e-05, -2.4585553488081233e-05], [-0.36932410587668785, -0.32592173578879674], [0.2502902408095952, 0.2208896007705616], [-0.20226779089499844, -0.19471673861434605], [-1.0741531957784514, -0.8793629942858061], [0.07320132820453645, 0.043167331882204465], [0.9784742829400376, 0.9517492682645989]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [22.402470691547656, 31.08271761800212], "std": [7.461557603692726, 9.80291946506434], "constant": [false, false]}} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/2/predictions.npz b/benchmarks/v1/evidence/multi-worker-045/A6/2/predictions.npz new file mode 100644 index 0000000..d0ddfa2 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/2/predictions.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/2/replay.npz b/benchmarks/v1/evidence/multi-worker-045/A6/2/replay.npz new file mode 100644 index 0000000..d0ddfa2 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/2/replay.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/2/training.json b/benchmarks/v1/evidence/multi-worker-045/A6/2/training.json new file mode 100644 index 0000000..0844d5b --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/2/training.json @@ -0,0 +1,33 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.394441268074094, + "last_score": 1.394441268074094, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "71b0d48c094e3fbaf6ba29a610f00f28884491bf358b6681b9527e0d2a2f4472", + "best_validation_score": 1.394441268074094, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/2/worker.log b/benchmarks/v1/evidence/multi-worker-045/A6/2/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/3/execution.json b/benchmarks/v1/evidence/multi-worker-045/A6/3/execution.json new file mode 100644 index 0000000..1b8e1dd --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/3/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "a512829556d1f79e6929ce52c4775026ae7f1eedecc38549c93c7dd9222c1d81", + "predictions.npz": "5e790851d06a4df9e9159dea50c0a844332276d1899025bd2390cfbc7a9e53e0", + "training.json": "39a854f84cdc8058a92256244585573a867a404f223e7e794d03f182ea2cdb17", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/3/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.4804589999985183 +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/3/model.bin b/benchmarks/v1/evidence/multi-worker-045/A6/3/model.bin new file mode 100644 index 0000000..7511eeb --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/3/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "base": [2.559870447126772e-14, 1.5040296482207727e-14], "terms": [], "classes": null}, "target_scale": {"mean": [21.36469700842268, 28.64957766482703], "std": [8.097653633950642, 10.41318564767784], "constant": [false, false]}} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/3/predictions.npz b/benchmarks/v1/evidence/multi-worker-045/A6/3/predictions.npz new file mode 100644 index 0000000..741de56 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/3/predictions.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/3/replay.npz b/benchmarks/v1/evidence/multi-worker-045/A6/3/replay.npz new file mode 100644 index 0000000..741de56 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/3/replay.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/3/training.json b/benchmarks/v1/evidence/multi-worker-045/A6/3/training.json new file mode 100644 index 0000000..f6a45a0 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/3/training.json @@ -0,0 +1,33 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.693465932244095, + "last_score": 0.7226623309509903, + "completed_rounds": 3, + "stale_rounds": 3, + "reason": "patience" + }, + "accepted_commits": 3, + "selected_model_identity": "9d21a60f7601a2d0b1588f6ccbc9edb76cc91fecf5ddddd4c91ffb5b2f5e3255", + "best_validation_score": 0.693465932244095, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/3/worker.log b/benchmarks/v1/evidence/multi-worker-045/A6/3/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/4/execution.json b/benchmarks/v1/evidence/multi-worker-045/A6/4/execution.json new file mode 100644 index 0000000..6270489 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/4/execution.json @@ -0,0 +1,19 @@ +{ + "artifacts": { + "model.bin": "1322a9e38866dd9df86971798d9ae14d0dd5edb50feb7445999062acff5ee51d", + "predictions.npz": "690d01a14687e851078ebbc99063c23469091691b704ed7027f03393f0bdaeff", + "training.json": "1751814014576b20ebc054323277dd5bf7507b1f37034501bf494b3d09c384fa", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/4/job.json" + ], + "exit_code": 0, + "reason": "", + "status": "pass", + "threads": 1, + "timeout_s": 90, + "wall_s": 1.9809298749896698 +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/4/model.bin b/benchmarks/v1/evidence/multi-worker-045/A6/4/model.bin new file mode 100644 index 0000000..b98af14 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/4/model.bin @@ -0,0 +1 @@ +{"format": "openboost-evaluation-v1", "application": "A6", "output": "multioutput_original_units", "model": {"format": "openboost-ensemble-v2", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "base": [1.5521085935596424e-14, 1.608182174667877e-14], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[49.0, 55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.607446875, 13.404562499999999, 19.682, 24.542125000000002, 28.7618125, 35.374, 41.30346875, 46.581250000000004, 50.70603125, 56.49, 62.303, 67.759, 72.414, 78.473, 84.58171875, 93.402, 98.90728125, 105.410625, 111.33, 117.37, 122.5, 126.8, 133.71, 140.35750000000002, 144.92, 150.58, 156.75, 162.35, 168.35, 174.66, 181.31], [0.00223, 0.00252, 0.00273, 0.00292, 0.00313, 0.00332, 0.00348, 0.00364, 0.00377, 0.00394, 0.00409, 0.00424, 0.0044, 0.004559375, 0.00473, 0.0049, 0.00508, 0.00526, 0.00546, 0.00571, 0.00593, 0.00614, 0.00642, 0.00677, 0.0071353125, 0.00759, 0.00814, 0.00883, 0.0099665625, 0.011804374999999999, 0.015199687500000001], [1.059125e-05, 1.2906875e-05, 1.455e-05, 1.630375e-05, 1.79190625e-05, 1.9096875e-05, 2.0359375e-05, 2.1805000000000002e-05, 2.32103125e-05, 2.4438125e-05, 2.58e-05, 2.715e-05, 2.8464687499999998e-05, 3.0278750000000002e-05, 3.1934375e-05, 3.345e-05, 3.516e-05, 3.7120625e-05, 3.8978437500000005e-05, 4.116125e-05, 4.3376249999999997e-05, 4.576e-05, 4.80284375e-05, 5.1102499999999994e-05, 5.41059375e-05, 5.808e-05, 6.2741875e-05, 6.83875e-05, 7.95765625e-05, 9.475375e-05, 0.0001276284375], [0.0009378125, 0.0011, 0.0012, 0.00129, 0.00137, 0.00146, 0.00154, 0.0016125, 0.0017, 0.00179, 0.0018559375000000002, 0.00193, 0.00201, 0.0020993749999999997, 0.00218, 0.002275, 0.00239, 0.0025, 0.00263, 0.00276, 0.00289, 0.00304, 0.0032, 0.00338, 0.00365, 0.0038831250000000003, 0.00417, 0.00458, 0.00518, 0.00604625, 0.0080865625], [0.00107, 0.00126, 0.00138, 0.00149125, 0.00158, 0.00167, 0.00175, 0.00183, 0.00191, 0.00198, 0.00206, 0.00213, 0.0022, 0.00228, 0.00237, 0.00247, 0.00258, 0.00269, 0.00279, 0.00291, 0.00302, 0.00315, 0.00329, 0.00343, 0.00361, 0.00391, 0.0042209375, 0.00465, 0.00528, 0.00607, 0.0077609375], [0.0028078125, 0.003295625, 0.0036, 0.00388, 0.00412, 0.00438, 0.00461, 0.0048425, 0.0051, 0.00536, 0.0055659375, 0.00578, 0.00604, 0.006289375, 0.00654, 0.0068249999999999995, 0.00717, 0.007490625, 0.0079, 0.00828, 0.00867, 0.00912, 0.0096096875, 0.010145, 0.0109453125, 0.011653125, 0.01251, 0.01374875, 0.01554, 0.018134375, 0.024249687500000002], [0.011036875, 0.01293, 0.0141534375, 0.01519, 0.01612, 0.016996875, 0.01788, 0.018725, 0.01962, 0.020488125000000003, 0.02142, 0.02226375, 0.02324, 0.024339375, 0.0250771875, 0.026205, 0.02737, 0.02833, 0.0297084375, 0.0311325, 0.032498124999999996, 0.034083749999999996, 0.0356890625, 0.03753, 0.039515312500000004, 0.041972499999999996, 0.0452209375, 0.048958749999999995, 0.0531165625, 0.059263125, 0.0727865625], [0.1, 0.118, 0.129, 0.138, 0.147, 0.155, 0.16346875, 0.171, 0.179, 0.1878125, 0.195, 0.204, 0.21215625, 0.222, 0.232, 0.242, 0.25128125, 0.262, 0.274, 0.286, 0.299, 0.311, 0.328, 0.344, 0.362, 0.381, 0.413, 0.448, 0.489, 0.5454375, 0.669], [0.00472, 0.005705625, 0.0065134375, 0.00709, 0.007578125, 0.008056875, 0.0084846875, 0.008955, 0.00944, 0.00986, 0.01035, 0.01082, 0.0112815625, 0.01182, 0.01233, 0.01294, 0.01362, 0.014160625000000001, 0.0147584375, 0.0156325, 0.0164440625, 0.017291874999999998, 0.018249374999999998, 0.0194725, 0.02056, 0.021919374999999998, 0.02376, 0.0254875, 0.02812625, 0.031651874999999996, 0.038384375], [0.00593, 0.00696, 0.0078334375, 0.00844, 0.009059062500000001, 0.00957, 0.01005, 0.01054, 0.01103, 0.011548125, 0.0120259375, 0.01268, 0.01327, 0.013859375, 0.01453, 0.015054999999999999, 0.0156928125, 0.0164, 0.01708, 0.01778, 0.01871, 0.01972, 0.02093, 0.0221425, 0.0234153125, 0.024909374999999997, 0.02661, 0.02899875, 0.0317628125, 0.03549, 0.0431753125], [0.0089378125, 0.010315625, 0.01139, 0.01242125, 0.0132690625, 0.01403, 0.01483, 0.01561, 0.01625, 0.017078125, 0.0178859375, 0.0186, 0.0192815625, 0.02006, 0.0209271875, 0.02174, 0.02257, 0.023480625, 0.0245284375, 0.02543875, 0.026418125, 0.02769, 0.02908, 0.0306775, 0.0323553125, 0.034115625, 0.036021875, 0.03895125, 0.042393125000000004, 0.048064375, 0.05792], [0.01417, 0.017115625, 0.0195403125, 0.02126, 0.0227353125, 0.02417375, 0.0254546875, 0.0268725, 0.02831, 0.02957, 0.031051875, 0.03245, 0.033853125, 0.035459375, 0.036987187500000004, 0.03882, 0.040855625, 0.0424725, 0.044276875, 0.046895, 0.049338125, 0.051865625000000005, 0.0547478125, 0.0584175, 0.06167, 0.06575125, 0.07128, 0.07645375, 0.0843853125, 0.09496125, 0.11515093750000001], [0.003821125, 0.0051125625, 0.00624178125, 0.00755125, 0.008322625, 0.009266, 0.010202, 0.011140500000000001, 0.011997375000000001, 0.012925062499999999, 0.013858500000000001, 0.014769375, 0.01585446875, 0.0168855, 0.01778359375, 0.018859, 0.019720375, 0.021057875, 0.022408, 0.023694125, 0.025430843749999998, 0.0274016875, 0.0293215625, 0.031514, 0.03382996875, 0.0363631875, 0.039581093750000004, 0.045060249999999996, 0.051140687500000004, 0.06174625, 0.09270975], [14.58659375, 15.760937499999999, 16.6820625, 17.384375, 17.97759375, 18.6186875, 19.04446875, 19.467, 19.8523125, 20.1664375, 20.481593750000002, 20.786625, 21.14940625, 21.505625000000002, 21.8174375, 22.137999999999998, 22.36228125, 22.6511875, 22.932375, 23.193625, 23.42440625, 23.7441875, 24.029, 24.31575, 24.63759375, 24.971, 25.322375, 25.689625, 26.195, 26.845375, 27.78259375], [0.3628290625, 0.3941425, 0.41482843750000004, 0.429425, 0.44026875, 0.45304875, 0.4639534375, 0.47317000000000004, 0.4840109375, 0.49284125, 0.501421875, 0.510615, 0.5190034375, 0.5276175, 0.5353471875, 0.5440149999999999, 0.55262, 0.561018125, 0.5693553124999999, 0.57774875, 0.587354375, 0.595669375, 0.6053181249999999, 0.6140475, 0.6228278125, 0.6332125, 0.644855625, 0.6586624999999999, 0.6717459375, 0.6920768749999999, 0.7167153125], [0.5387790625, 0.5512475, 0.56081375, 0.56941875, 0.576210625, 0.5830275, 0.5907693749999999, 0.597565, 0.6053006249999999, 0.6110225, 0.6181178125, 0.62450375, 0.6315659375, 0.6378024999999999, 0.6458915625, 0.6539649999999999, 0.6628578125, 0.673354375, 0.6831021875, 0.6952825, 0.70391625, 0.7113318750000001, 0.7184796875, 0.7257075000000001, 0.7332765625, 0.73873, 0.7456246875, 0.752935, 0.76121, 0.771455625, 0.7882865625], [0.092377, 0.10926, 0.1205134375, 0.13047625000000002, 0.1386, 0.14554625, 0.15279937500000002, 0.16017, 0.16631125000000002, 0.17144437499999998, 0.17676781249999998, 0.18289124999999998, 0.188403125, 0.193388125, 0.20009749999999998, 0.205835, 0.2117440625, 0.216980625, 0.222796875, 0.23012875000000002, 0.2368009375, 0.244301875, 0.252146875, 0.26049, 0.27062125, 0.282998125, 0.29854125, 0.31872375000000003, 0.34191218749999996, 0.376975625, 0.4302], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 17, 0, -1, -1, -1, -1], "threshold": [10, 13, 16, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.1378443627484766e-15, 9.616142243161562e-17], [-0.27622982470852253, -0.30740369797612677], [0.5297652488808289, 0.5895518224255807], [0.10556390277741336, 0.014591050197722576], [-0.532030655114766, -0.5230855784727709], [0.6929224289139588, 0.7084345719531541], [-0.6867791400988827, -0.2982937340096445]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[49.0, 55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.607446875, 13.404562499999999, 19.682, 24.542125000000002, 28.7618125, 35.374, 41.30346875, 46.581250000000004, 50.70603125, 56.49, 62.303, 67.759, 72.414, 78.473, 84.58171875, 93.402, 98.90728125, 105.410625, 111.33, 117.37, 122.5, 126.8, 133.71, 140.35750000000002, 144.92, 150.58, 156.75, 162.35, 168.35, 174.66, 181.31], [0.00223, 0.00252, 0.00273, 0.00292, 0.00313, 0.00332, 0.00348, 0.00364, 0.00377, 0.00394, 0.00409, 0.00424, 0.0044, 0.004559375, 0.00473, 0.0049, 0.00508, 0.00526, 0.00546, 0.00571, 0.00593, 0.00614, 0.00642, 0.00677, 0.0071353125, 0.00759, 0.00814, 0.00883, 0.0099665625, 0.011804374999999999, 0.015199687500000001], [1.059125e-05, 1.2906875e-05, 1.455e-05, 1.630375e-05, 1.79190625e-05, 1.9096875e-05, 2.0359375e-05, 2.1805000000000002e-05, 2.32103125e-05, 2.4438125e-05, 2.58e-05, 2.715e-05, 2.8464687499999998e-05, 3.0278750000000002e-05, 3.1934375e-05, 3.345e-05, 3.516e-05, 3.7120625e-05, 3.8978437500000005e-05, 4.116125e-05, 4.3376249999999997e-05, 4.576e-05, 4.80284375e-05, 5.1102499999999994e-05, 5.41059375e-05, 5.808e-05, 6.2741875e-05, 6.83875e-05, 7.95765625e-05, 9.475375e-05, 0.0001276284375], [0.0009378125, 0.0011, 0.0012, 0.00129, 0.00137, 0.00146, 0.00154, 0.0016125, 0.0017, 0.00179, 0.0018559375000000002, 0.00193, 0.00201, 0.0020993749999999997, 0.00218, 0.002275, 0.00239, 0.0025, 0.00263, 0.00276, 0.00289, 0.00304, 0.0032, 0.00338, 0.00365, 0.0038831250000000003, 0.00417, 0.00458, 0.00518, 0.00604625, 0.0080865625], [0.00107, 0.00126, 0.00138, 0.00149125, 0.00158, 0.00167, 0.00175, 0.00183, 0.00191, 0.00198, 0.00206, 0.00213, 0.0022, 0.00228, 0.00237, 0.00247, 0.00258, 0.00269, 0.00279, 0.00291, 0.00302, 0.00315, 0.00329, 0.00343, 0.00361, 0.00391, 0.0042209375, 0.00465, 0.00528, 0.00607, 0.0077609375], [0.0028078125, 0.003295625, 0.0036, 0.00388, 0.00412, 0.00438, 0.00461, 0.0048425, 0.0051, 0.00536, 0.0055659375, 0.00578, 0.00604, 0.006289375, 0.00654, 0.0068249999999999995, 0.00717, 0.007490625, 0.0079, 0.00828, 0.00867, 0.00912, 0.0096096875, 0.010145, 0.0109453125, 0.011653125, 0.01251, 0.01374875, 0.01554, 0.018134375, 0.024249687500000002], [0.011036875, 0.01293, 0.0141534375, 0.01519, 0.01612, 0.016996875, 0.01788, 0.018725, 0.01962, 0.020488125000000003, 0.02142, 0.02226375, 0.02324, 0.024339375, 0.0250771875, 0.026205, 0.02737, 0.02833, 0.0297084375, 0.0311325, 0.032498124999999996, 0.034083749999999996, 0.0356890625, 0.03753, 0.039515312500000004, 0.041972499999999996, 0.0452209375, 0.048958749999999995, 0.0531165625, 0.059263125, 0.0727865625], [0.1, 0.118, 0.129, 0.138, 0.147, 0.155, 0.16346875, 0.171, 0.179, 0.1878125, 0.195, 0.204, 0.21215625, 0.222, 0.232, 0.242, 0.25128125, 0.262, 0.274, 0.286, 0.299, 0.311, 0.328, 0.344, 0.362, 0.381, 0.413, 0.448, 0.489, 0.5454375, 0.669], [0.00472, 0.005705625, 0.0065134375, 0.00709, 0.007578125, 0.008056875, 0.0084846875, 0.008955, 0.00944, 0.00986, 0.01035, 0.01082, 0.0112815625, 0.01182, 0.01233, 0.01294, 0.01362, 0.014160625000000001, 0.0147584375, 0.0156325, 0.0164440625, 0.017291874999999998, 0.018249374999999998, 0.0194725, 0.02056, 0.021919374999999998, 0.02376, 0.0254875, 0.02812625, 0.031651874999999996, 0.038384375], [0.00593, 0.00696, 0.0078334375, 0.00844, 0.009059062500000001, 0.00957, 0.01005, 0.01054, 0.01103, 0.011548125, 0.0120259375, 0.01268, 0.01327, 0.013859375, 0.01453, 0.015054999999999999, 0.0156928125, 0.0164, 0.01708, 0.01778, 0.01871, 0.01972, 0.02093, 0.0221425, 0.0234153125, 0.024909374999999997, 0.02661, 0.02899875, 0.0317628125, 0.03549, 0.0431753125], [0.0089378125, 0.010315625, 0.01139, 0.01242125, 0.0132690625, 0.01403, 0.01483, 0.01561, 0.01625, 0.017078125, 0.0178859375, 0.0186, 0.0192815625, 0.02006, 0.0209271875, 0.02174, 0.02257, 0.023480625, 0.0245284375, 0.02543875, 0.026418125, 0.02769, 0.02908, 0.0306775, 0.0323553125, 0.034115625, 0.036021875, 0.03895125, 0.042393125000000004, 0.048064375, 0.05792], [0.01417, 0.017115625, 0.0195403125, 0.02126, 0.0227353125, 0.02417375, 0.0254546875, 0.0268725, 0.02831, 0.02957, 0.031051875, 0.03245, 0.033853125, 0.035459375, 0.036987187500000004, 0.03882, 0.040855625, 0.0424725, 0.044276875, 0.046895, 0.049338125, 0.051865625000000005, 0.0547478125, 0.0584175, 0.06167, 0.06575125, 0.07128, 0.07645375, 0.0843853125, 0.09496125, 0.11515093750000001], [0.003821125, 0.0051125625, 0.00624178125, 0.00755125, 0.008322625, 0.009266, 0.010202, 0.011140500000000001, 0.011997375000000001, 0.012925062499999999, 0.013858500000000001, 0.014769375, 0.01585446875, 0.0168855, 0.01778359375, 0.018859, 0.019720375, 0.021057875, 0.022408, 0.023694125, 0.025430843749999998, 0.0274016875, 0.0293215625, 0.031514, 0.03382996875, 0.0363631875, 0.039581093750000004, 0.045060249999999996, 0.051140687500000004, 0.06174625, 0.09270975], [14.58659375, 15.760937499999999, 16.6820625, 17.384375, 17.97759375, 18.6186875, 19.04446875, 19.467, 19.8523125, 20.1664375, 20.481593750000002, 20.786625, 21.14940625, 21.505625000000002, 21.8174375, 22.137999999999998, 22.36228125, 22.6511875, 22.932375, 23.193625, 23.42440625, 23.7441875, 24.029, 24.31575, 24.63759375, 24.971, 25.322375, 25.689625, 26.195, 26.845375, 27.78259375], [0.3628290625, 0.3941425, 0.41482843750000004, 0.429425, 0.44026875, 0.45304875, 0.4639534375, 0.47317000000000004, 0.4840109375, 0.49284125, 0.501421875, 0.510615, 0.5190034375, 0.5276175, 0.5353471875, 0.5440149999999999, 0.55262, 0.561018125, 0.5693553124999999, 0.57774875, 0.587354375, 0.595669375, 0.6053181249999999, 0.6140475, 0.6228278125, 0.6332125, 0.644855625, 0.6586624999999999, 0.6717459375, 0.6920768749999999, 0.7167153125], [0.5387790625, 0.5512475, 0.56081375, 0.56941875, 0.576210625, 0.5830275, 0.5907693749999999, 0.597565, 0.6053006249999999, 0.6110225, 0.6181178125, 0.62450375, 0.6315659375, 0.6378024999999999, 0.6458915625, 0.6539649999999999, 0.6628578125, 0.673354375, 0.6831021875, 0.6952825, 0.70391625, 0.7113318750000001, 0.7184796875, 0.7257075000000001, 0.7332765625, 0.73873, 0.7456246875, 0.752935, 0.76121, 0.771455625, 0.7882865625], [0.092377, 0.10926, 0.1205134375, 0.13047625000000002, 0.1386, 0.14554625, 0.15279937500000002, 0.16017, 0.16631125000000002, 0.17144437499999998, 0.17676781249999998, 0.18289124999999998, 0.188403125, 0.193388125, 0.20009749999999998, 0.205835, 0.2117440625, 0.216980625, 0.222796875, 0.23012875000000002, 0.2368009375, 0.244301875, 0.252146875, 0.26049, 0.27062125, 0.282998125, 0.29854125, 0.31872375000000003, 0.34191218749999996, 0.376975625, 0.4302], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 17, 0, -1, -1, -1, -1], "threshold": [10, 26, 16, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-1.195799327214806e-05, -2.7981135235140753e-06], [-0.24862529601319636, -0.2766853314034485], [0.4767892338092447, 0.5306306767670853], [-0.11829528661389058, -0.15049094296190327], [-0.8267055370496044, -0.836337391774727], [0.623695371575613, 0.6376577595905734], [-0.6185814912219304, -0.2686729576254701]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[49.0, 55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.607446875, 13.404562499999999, 19.682, 24.542125000000002, 28.7618125, 35.374, 41.30346875, 46.581250000000004, 50.70603125, 56.49, 62.303, 67.759, 72.414, 78.473, 84.58171875, 93.402, 98.90728125, 105.410625, 111.33, 117.37, 122.5, 126.8, 133.71, 140.35750000000002, 144.92, 150.58, 156.75, 162.35, 168.35, 174.66, 181.31], [0.00223, 0.00252, 0.00273, 0.00292, 0.00313, 0.00332, 0.00348, 0.00364, 0.00377, 0.00394, 0.00409, 0.00424, 0.0044, 0.004559375, 0.00473, 0.0049, 0.00508, 0.00526, 0.00546, 0.00571, 0.00593, 0.00614, 0.00642, 0.00677, 0.0071353125, 0.00759, 0.00814, 0.00883, 0.0099665625, 0.011804374999999999, 0.015199687500000001], [1.059125e-05, 1.2906875e-05, 1.455e-05, 1.630375e-05, 1.79190625e-05, 1.9096875e-05, 2.0359375e-05, 2.1805000000000002e-05, 2.32103125e-05, 2.4438125e-05, 2.58e-05, 2.715e-05, 2.8464687499999998e-05, 3.0278750000000002e-05, 3.1934375e-05, 3.345e-05, 3.516e-05, 3.7120625e-05, 3.8978437500000005e-05, 4.116125e-05, 4.3376249999999997e-05, 4.576e-05, 4.80284375e-05, 5.1102499999999994e-05, 5.41059375e-05, 5.808e-05, 6.2741875e-05, 6.83875e-05, 7.95765625e-05, 9.475375e-05, 0.0001276284375], [0.0009378125, 0.0011, 0.0012, 0.00129, 0.00137, 0.00146, 0.00154, 0.0016125, 0.0017, 0.00179, 0.0018559375000000002, 0.00193, 0.00201, 0.0020993749999999997, 0.00218, 0.002275, 0.00239, 0.0025, 0.00263, 0.00276, 0.00289, 0.00304, 0.0032, 0.00338, 0.00365, 0.0038831250000000003, 0.00417, 0.00458, 0.00518, 0.00604625, 0.0080865625], [0.00107, 0.00126, 0.00138, 0.00149125, 0.00158, 0.00167, 0.00175, 0.00183, 0.00191, 0.00198, 0.00206, 0.00213, 0.0022, 0.00228, 0.00237, 0.00247, 0.00258, 0.00269, 0.00279, 0.00291, 0.00302, 0.00315, 0.00329, 0.00343, 0.00361, 0.00391, 0.0042209375, 0.00465, 0.00528, 0.00607, 0.0077609375], [0.0028078125, 0.003295625, 0.0036, 0.00388, 0.00412, 0.00438, 0.00461, 0.0048425, 0.0051, 0.00536, 0.0055659375, 0.00578, 0.00604, 0.006289375, 0.00654, 0.0068249999999999995, 0.00717, 0.007490625, 0.0079, 0.00828, 0.00867, 0.00912, 0.0096096875, 0.010145, 0.0109453125, 0.011653125, 0.01251, 0.01374875, 0.01554, 0.018134375, 0.024249687500000002], [0.011036875, 0.01293, 0.0141534375, 0.01519, 0.01612, 0.016996875, 0.01788, 0.018725, 0.01962, 0.020488125000000003, 0.02142, 0.02226375, 0.02324, 0.024339375, 0.0250771875, 0.026205, 0.02737, 0.02833, 0.0297084375, 0.0311325, 0.032498124999999996, 0.034083749999999996, 0.0356890625, 0.03753, 0.039515312500000004, 0.041972499999999996, 0.0452209375, 0.048958749999999995, 0.0531165625, 0.059263125, 0.0727865625], [0.1, 0.118, 0.129, 0.138, 0.147, 0.155, 0.16346875, 0.171, 0.179, 0.1878125, 0.195, 0.204, 0.21215625, 0.222, 0.232, 0.242, 0.25128125, 0.262, 0.274, 0.286, 0.299, 0.311, 0.328, 0.344, 0.362, 0.381, 0.413, 0.448, 0.489, 0.5454375, 0.669], [0.00472, 0.005705625, 0.0065134375, 0.00709, 0.007578125, 0.008056875, 0.0084846875, 0.008955, 0.00944, 0.00986, 0.01035, 0.01082, 0.0112815625, 0.01182, 0.01233, 0.01294, 0.01362, 0.014160625000000001, 0.0147584375, 0.0156325, 0.0164440625, 0.017291874999999998, 0.018249374999999998, 0.0194725, 0.02056, 0.021919374999999998, 0.02376, 0.0254875, 0.02812625, 0.031651874999999996, 0.038384375], [0.00593, 0.00696, 0.0078334375, 0.00844, 0.009059062500000001, 0.00957, 0.01005, 0.01054, 0.01103, 0.011548125, 0.0120259375, 0.01268, 0.01327, 0.013859375, 0.01453, 0.015054999999999999, 0.0156928125, 0.0164, 0.01708, 0.01778, 0.01871, 0.01972, 0.02093, 0.0221425, 0.0234153125, 0.024909374999999997, 0.02661, 0.02899875, 0.0317628125, 0.03549, 0.0431753125], [0.0089378125, 0.010315625, 0.01139, 0.01242125, 0.0132690625, 0.01403, 0.01483, 0.01561, 0.01625, 0.017078125, 0.0178859375, 0.0186, 0.0192815625, 0.02006, 0.0209271875, 0.02174, 0.02257, 0.023480625, 0.0245284375, 0.02543875, 0.026418125, 0.02769, 0.02908, 0.0306775, 0.0323553125, 0.034115625, 0.036021875, 0.03895125, 0.042393125000000004, 0.048064375, 0.05792], [0.01417, 0.017115625, 0.0195403125, 0.02126, 0.0227353125, 0.02417375, 0.0254546875, 0.0268725, 0.02831, 0.02957, 0.031051875, 0.03245, 0.033853125, 0.035459375, 0.036987187500000004, 0.03882, 0.040855625, 0.0424725, 0.044276875, 0.046895, 0.049338125, 0.051865625000000005, 0.0547478125, 0.0584175, 0.06167, 0.06575125, 0.07128, 0.07645375, 0.0843853125, 0.09496125, 0.11515093750000001], [0.003821125, 0.0051125625, 0.00624178125, 0.00755125, 0.008322625, 0.009266, 0.010202, 0.011140500000000001, 0.011997375000000001, 0.012925062499999999, 0.013858500000000001, 0.014769375, 0.01585446875, 0.0168855, 0.01778359375, 0.018859, 0.019720375, 0.021057875, 0.022408, 0.023694125, 0.025430843749999998, 0.0274016875, 0.0293215625, 0.031514, 0.03382996875, 0.0363631875, 0.039581093750000004, 0.045060249999999996, 0.051140687500000004, 0.06174625, 0.09270975], [14.58659375, 15.760937499999999, 16.6820625, 17.384375, 17.97759375, 18.6186875, 19.04446875, 19.467, 19.8523125, 20.1664375, 20.481593750000002, 20.786625, 21.14940625, 21.505625000000002, 21.8174375, 22.137999999999998, 22.36228125, 22.6511875, 22.932375, 23.193625, 23.42440625, 23.7441875, 24.029, 24.31575, 24.63759375, 24.971, 25.322375, 25.689625, 26.195, 26.845375, 27.78259375], [0.3628290625, 0.3941425, 0.41482843750000004, 0.429425, 0.44026875, 0.45304875, 0.4639534375, 0.47317000000000004, 0.4840109375, 0.49284125, 0.501421875, 0.510615, 0.5190034375, 0.5276175, 0.5353471875, 0.5440149999999999, 0.55262, 0.561018125, 0.5693553124999999, 0.57774875, 0.587354375, 0.595669375, 0.6053181249999999, 0.6140475, 0.6228278125, 0.6332125, 0.644855625, 0.6586624999999999, 0.6717459375, 0.6920768749999999, 0.7167153125], [0.5387790625, 0.5512475, 0.56081375, 0.56941875, 0.576210625, 0.5830275, 0.5907693749999999, 0.597565, 0.6053006249999999, 0.6110225, 0.6181178125, 0.62450375, 0.6315659375, 0.6378024999999999, 0.6458915625, 0.6539649999999999, 0.6628578125, 0.673354375, 0.6831021875, 0.6952825, 0.70391625, 0.7113318750000001, 0.7184796875, 0.7257075000000001, 0.7332765625, 0.73873, 0.7456246875, 0.752935, 0.76121, 0.771455625, 0.7882865625], [0.092377, 0.10926, 0.1205134375, 0.13047625000000002, 0.1386, 0.14554625, 0.15279937500000002, 0.16017, 0.16631125000000002, 0.17144437499999998, 0.17676781249999998, 0.18289124999999998, 0.188403125, 0.193388125, 0.20009749999999998, 0.205835, 0.2117440625, 0.216980625, 0.222796875, 0.23012875000000002, 0.2368009375, 0.244301875, 0.252146875, 0.26049, 0.27062125, 0.282998125, 0.29854125, 0.31872375000000003, 0.34191218749999996, 0.376975625, 0.4302], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 17, 0, -1, -1, -1, -1], "threshold": [10, 13, 16, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-3.750150954450774e-05, -2.0095643074146278e-05], [-0.22380365783652298, -0.24905949961899884], [0.42911073481673023, 0.4775982302357678], [0.10683566924469916, 0.028166395089144973], [-0.4453423080341683, -0.43476511435132315], [0.5613845075480203, 0.5739519702504804], [-0.5571559165691241, -0.24199354505007348]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37"], "cuts": [[49.0, 55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 66.0, 67.0, 68.0, 72.0, 73.0, 74.0, 76.0, 78.0], [0.0], [7.607446875, 13.404562499999999, 19.682, 24.542125000000002, 28.7618125, 35.374, 41.30346875, 46.581250000000004, 50.70603125, 56.49, 62.303, 67.759, 72.414, 78.473, 84.58171875, 93.402, 98.90728125, 105.410625, 111.33, 117.37, 122.5, 126.8, 133.71, 140.35750000000002, 144.92, 150.58, 156.75, 162.35, 168.35, 174.66, 181.31], [0.00223, 0.00252, 0.00273, 0.00292, 0.00313, 0.00332, 0.00348, 0.00364, 0.00377, 0.00394, 0.00409, 0.00424, 0.0044, 0.004559375, 0.00473, 0.0049, 0.00508, 0.00526, 0.00546, 0.00571, 0.00593, 0.00614, 0.00642, 0.00677, 0.0071353125, 0.00759, 0.00814, 0.00883, 0.0099665625, 0.011804374999999999, 0.015199687500000001], [1.059125e-05, 1.2906875e-05, 1.455e-05, 1.630375e-05, 1.79190625e-05, 1.9096875e-05, 2.0359375e-05, 2.1805000000000002e-05, 2.32103125e-05, 2.4438125e-05, 2.58e-05, 2.715e-05, 2.8464687499999998e-05, 3.0278750000000002e-05, 3.1934375e-05, 3.345e-05, 3.516e-05, 3.7120625e-05, 3.8978437500000005e-05, 4.116125e-05, 4.3376249999999997e-05, 4.576e-05, 4.80284375e-05, 5.1102499999999994e-05, 5.41059375e-05, 5.808e-05, 6.2741875e-05, 6.83875e-05, 7.95765625e-05, 9.475375e-05, 0.0001276284375], [0.0009378125, 0.0011, 0.0012, 0.00129, 0.00137, 0.00146, 0.00154, 0.0016125, 0.0017, 0.00179, 0.0018559375000000002, 0.00193, 0.00201, 0.0020993749999999997, 0.00218, 0.002275, 0.00239, 0.0025, 0.00263, 0.00276, 0.00289, 0.00304, 0.0032, 0.00338, 0.00365, 0.0038831250000000003, 0.00417, 0.00458, 0.00518, 0.00604625, 0.0080865625], [0.00107, 0.00126, 0.00138, 0.00149125, 0.00158, 0.00167, 0.00175, 0.00183, 0.00191, 0.00198, 0.00206, 0.00213, 0.0022, 0.00228, 0.00237, 0.00247, 0.00258, 0.00269, 0.00279, 0.00291, 0.00302, 0.00315, 0.00329, 0.00343, 0.00361, 0.00391, 0.0042209375, 0.00465, 0.00528, 0.00607, 0.0077609375], [0.0028078125, 0.003295625, 0.0036, 0.00388, 0.00412, 0.00438, 0.00461, 0.0048425, 0.0051, 0.00536, 0.0055659375, 0.00578, 0.00604, 0.006289375, 0.00654, 0.0068249999999999995, 0.00717, 0.007490625, 0.0079, 0.00828, 0.00867, 0.00912, 0.0096096875, 0.010145, 0.0109453125, 0.011653125, 0.01251, 0.01374875, 0.01554, 0.018134375, 0.024249687500000002], [0.011036875, 0.01293, 0.0141534375, 0.01519, 0.01612, 0.016996875, 0.01788, 0.018725, 0.01962, 0.020488125000000003, 0.02142, 0.02226375, 0.02324, 0.024339375, 0.0250771875, 0.026205, 0.02737, 0.02833, 0.0297084375, 0.0311325, 0.032498124999999996, 0.034083749999999996, 0.0356890625, 0.03753, 0.039515312500000004, 0.041972499999999996, 0.0452209375, 0.048958749999999995, 0.0531165625, 0.059263125, 0.0727865625], [0.1, 0.118, 0.129, 0.138, 0.147, 0.155, 0.16346875, 0.171, 0.179, 0.1878125, 0.195, 0.204, 0.21215625, 0.222, 0.232, 0.242, 0.25128125, 0.262, 0.274, 0.286, 0.299, 0.311, 0.328, 0.344, 0.362, 0.381, 0.413, 0.448, 0.489, 0.5454375, 0.669], [0.00472, 0.005705625, 0.0065134375, 0.00709, 0.007578125, 0.008056875, 0.0084846875, 0.008955, 0.00944, 0.00986, 0.01035, 0.01082, 0.0112815625, 0.01182, 0.01233, 0.01294, 0.01362, 0.014160625000000001, 0.0147584375, 0.0156325, 0.0164440625, 0.017291874999999998, 0.018249374999999998, 0.0194725, 0.02056, 0.021919374999999998, 0.02376, 0.0254875, 0.02812625, 0.031651874999999996, 0.038384375], [0.00593, 0.00696, 0.0078334375, 0.00844, 0.009059062500000001, 0.00957, 0.01005, 0.01054, 0.01103, 0.011548125, 0.0120259375, 0.01268, 0.01327, 0.013859375, 0.01453, 0.015054999999999999, 0.0156928125, 0.0164, 0.01708, 0.01778, 0.01871, 0.01972, 0.02093, 0.0221425, 0.0234153125, 0.024909374999999997, 0.02661, 0.02899875, 0.0317628125, 0.03549, 0.0431753125], [0.0089378125, 0.010315625, 0.01139, 0.01242125, 0.0132690625, 0.01403, 0.01483, 0.01561, 0.01625, 0.017078125, 0.0178859375, 0.0186, 0.0192815625, 0.02006, 0.0209271875, 0.02174, 0.02257, 0.023480625, 0.0245284375, 0.02543875, 0.026418125, 0.02769, 0.02908, 0.0306775, 0.0323553125, 0.034115625, 0.036021875, 0.03895125, 0.042393125000000004, 0.048064375, 0.05792], [0.01417, 0.017115625, 0.0195403125, 0.02126, 0.0227353125, 0.02417375, 0.0254546875, 0.0268725, 0.02831, 0.02957, 0.031051875, 0.03245, 0.033853125, 0.035459375, 0.036987187500000004, 0.03882, 0.040855625, 0.0424725, 0.044276875, 0.046895, 0.049338125, 0.051865625000000005, 0.0547478125, 0.0584175, 0.06167, 0.06575125, 0.07128, 0.07645375, 0.0843853125, 0.09496125, 0.11515093750000001], [0.003821125, 0.0051125625, 0.00624178125, 0.00755125, 0.008322625, 0.009266, 0.010202, 0.011140500000000001, 0.011997375000000001, 0.012925062499999999, 0.013858500000000001, 0.014769375, 0.01585446875, 0.0168855, 0.01778359375, 0.018859, 0.019720375, 0.021057875, 0.022408, 0.023694125, 0.025430843749999998, 0.0274016875, 0.0293215625, 0.031514, 0.03382996875, 0.0363631875, 0.039581093750000004, 0.045060249999999996, 0.051140687500000004, 0.06174625, 0.09270975], [14.58659375, 15.760937499999999, 16.6820625, 17.384375, 17.97759375, 18.6186875, 19.04446875, 19.467, 19.8523125, 20.1664375, 20.481593750000002, 20.786625, 21.14940625, 21.505625000000002, 21.8174375, 22.137999999999998, 22.36228125, 22.6511875, 22.932375, 23.193625, 23.42440625, 23.7441875, 24.029, 24.31575, 24.63759375, 24.971, 25.322375, 25.689625, 26.195, 26.845375, 27.78259375], [0.3628290625, 0.3941425, 0.41482843750000004, 0.429425, 0.44026875, 0.45304875, 0.4639534375, 0.47317000000000004, 0.4840109375, 0.49284125, 0.501421875, 0.510615, 0.5190034375, 0.5276175, 0.5353471875, 0.5440149999999999, 0.55262, 0.561018125, 0.5693553124999999, 0.57774875, 0.587354375, 0.595669375, 0.6053181249999999, 0.6140475, 0.6228278125, 0.6332125, 0.644855625, 0.6586624999999999, 0.6717459375, 0.6920768749999999, 0.7167153125], [0.5387790625, 0.5512475, 0.56081375, 0.56941875, 0.576210625, 0.5830275, 0.5907693749999999, 0.597565, 0.6053006249999999, 0.6110225, 0.6181178125, 0.62450375, 0.6315659375, 0.6378024999999999, 0.6458915625, 0.6539649999999999, 0.6628578125, 0.673354375, 0.6831021875, 0.6952825, 0.70391625, 0.7113318750000001, 0.7184796875, 0.7257075000000001, 0.7332765625, 0.73873, 0.7456246875, 0.752935, 0.76121, 0.771455625, 0.7882865625], [0.092377, 0.10926, 0.1205134375, 0.13047625000000002, 0.1386, 0.14554625, 0.15279937500000002, 0.16017, 0.16631125000000002, 0.17144437499999998, 0.17676781249999998, 0.18289124999999998, 0.188403125, 0.193388125, 0.20009749999999998, 0.205835, 0.2117440625, 0.216980625, 0.222796875, 0.23012875000000002, 0.2368009375, 0.244301875, 0.252146875, 0.26049, 0.27062125, 0.282998125, 0.29854125, 0.31872375000000003, 0.34191218749999996, 0.376975625, 0.4302], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "categories": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [10, 2, 15, -1, -1, -1, -1], "missing_left": [false, false, false, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-4.326140262529378e-05, -2.020955797231533e-05], [-0.2014379396789565, -0.2241711437167828], [0.3862000122554706, 0.4298659556292208], [0.13923602474658905, 0.13417013809479056], [-0.3697728749380809, -0.4012279694712336], [0.5584590035371918, 0.5965043358270463], [-0.11077103986477065, -0.05107043097104927]]}, "mapping": [[1.0, 0.0], [0.0, 1.0]], "coefficient": 0.1}], "classes": null}, "target_scale": {"mean": [21.608206573705065, 28.812343511667468], "std": [7.37511081258478, 9.516732486364944], "constant": [false, false]}} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/4/predictions.npz b/benchmarks/v1/evidence/multi-worker-045/A6/4/predictions.npz new file mode 100644 index 0000000..3fe7264 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/4/predictions.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/4/replay.npz b/benchmarks/v1/evidence/multi-worker-045/A6/4/replay.npz new file mode 100644 index 0000000..3fe7264 Binary files /dev/null and b/benchmarks/v1/evidence/multi-worker-045/A6/4/replay.npz differ diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/4/training.json b/benchmarks/v1/evidence/multi-worker-045/A6/4/training.json new file mode 100644 index 0000000..040160e --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/A6/4/training.json @@ -0,0 +1,33 @@ +{ + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.6656270352836269, + "last_score": 1.6656270352836269, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "34a80e1291113ded1ab48b3944f698908b0a717f69b631e66c0e5b2147aed920", + "best_validation_score": 1.6656270352836269, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" +} diff --git a/benchmarks/v1/evidence/multi-worker-045/A6/4/worker.log b/benchmarks/v1/evidence/multi-worker-045/A6/4/worker.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/v1/evidence/multi-worker-045/README.md b/benchmarks/v1/evidence/multi-worker-045/README.md new file mode 100644 index 0000000..f72d76a --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/README.md @@ -0,0 +1,18 @@ +# A6 current worker integration + +Five grouped Parkinsons folds pass using four-round shared multi-output trees, +32 bins, patience three and one CPU thread. Saved scale metadata exactly equals +the frozen unweighted training-population scale. Fresh-process original-unit +predictions match exactly. No test scores, search or performance claim. + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-multi-worker-045 --applications A6 +``` + +Requires the pinned local data and committed preprocessing freeze. The summary +records source/revision/environment/input identity, exact jobs, model/validation +artifact hashes and process outcomes. Each fold retains raw predictions, JSON +model and scale, training state metadata, process record/log and exact replay. +Generated source packets can be reconstructed with worker_data.export; hashes +are retained in the summary. Independent-tree mode has synthetic unit coverage, +not real five-fold evidence in this directory. No memory cap or CUDA was used. diff --git a/benchmarks/v1/evidence/multi-worker-045/summary.json b/benchmarks/v1/evidence/multi-worker-045/summary.json new file mode 100644 index 0000000..f278d57 --- /dev/null +++ b/benchmarks/v1/evidence/multi-worker-045/summary.json @@ -0,0 +1,686 @@ +{ + "scope": "Current A1/A6/A11 real-data validation plumbing only; four rounds, no test scores, quality or performance claim", + "revision": "374ab87cbc5083322b08a5f23ab74cf79507e7f3", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "benchmarks.v1.openboost_worker_smoke", + "/private/tmp/openboost-multi-worker-045-final", + "--applications", + "A6" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "gpu": null, + "threads": 1, + "memory_cap": null, + "packages": { + "numpy": "2.3.5", + "openboost": "1.0.0rc1" + }, + "sources": { + "src/openboost/__init__.py": "b0a76b68a1824abef45bf30d3d094dce7de2ec9deef3d5cbe862a6b3f6038788", + "src/openboost/artifacts.py": "fc5232f29d2b9d55676035bbd5bb9fe726babba5fcdfbd84ff18133de3e9a817", + "src/openboost/binning.py": "e77bdbaf42121ba5dde8ec681371bd0c7d1e31fada19618d796279cc0b322a7d", + "src/openboost/composition.py": "a52dc4d09582031441dbf2c4a3ed889851783a2f4fece50506fa5e0e0501ccec", + "src/openboost/data.py": "93b4eb6487e7fb21c960d19906c7a49b3df9fa28b2a2bacdc8ea6437889f2343", + "src/openboost/leaves.py": "3bff04ff4b1eff1a1d58f6ce5e29f15391e8b2e9a80a696c6fee988d1be95b75", + "src/openboost/multioutput.py": "98c192719bb9ee787ec385a2d219a2f52aacebc161cc9fc1275006f49daad76d", + "src/openboost/objectives.py": "fcf847bda2917354093e03ce42bd22e0fd85e391185d3141318cb7ccecd721f7", + "src/openboost/ops.py": "aa73cc40df7c79203505f91e3b86292dffbb3b9348467be60d8587e3bfd8c6ad", + "src/openboost/outputs.py": "f10f3a68b4513cb93c222cd1e39ca238c686c903daa8c8035c417b5916365115", + "src/openboost/ranking.py": "beddf8fb6eeecbed54acdb55147bc251392a6f9fb7b46304e5d80d1d0ee642b3", + "src/openboost/recipes.py": "4fc1cd9971cde4bad95922c0f4e241d169a8a9fa08730070f02033c3405d265c", + "src/openboost/results.py": "b33bb8c658678bf5319a1e76b9a420112debea4f26361acad166f6f1e641585d", + "src/openboost/runs.py": "5a00eead8d6d4d6ddc99a646a0131f04918c619c5061b647ec4fc3c386459dc4", + "src/openboost/runtime.py": "8962e0204d867a1b700ed15e92b155cd6144927b651a79b8083634a0c315ad2d", + "src/openboost/stats.py": "4edbeba39e727f60213f04b5d3592bc82fb02cb1ebd9603ca9f8cee66b932cc9", + "src/openboost/stopping.py": "2374873a01dba8d7b359cd484337de228ca6c805d6a43b23bcf51598416746a1", + "src/openboost/survival.py": "cb168551a5aa026d6c501f4370c7ddc998d3cc7269ce6f17d93258ceadef89e9", + "src/openboost/tree.py": "60dd8cbc56d78913e868a3788bd08c91e858febc4f4c0534831077df640d7581", + "benchmarks/v1/openboost_worker.py": "a467fc8024eebad0fa9e1412e20e9c374f72cb96b35b08890434a669a44b4085", + "benchmarks/v1/openboost_predict.py": "844af41520e2f6de1e0c7f7d50bea13777867b860de83f385e529780661903c9", + "benchmarks/v1/openboost_worker_smoke.py": "1591fefe496a08008c248a1003676aa25dc7eb7d36a428a58af77cad8477da9a", + "benchmarks/v1/worker_data.py": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "benchmarks/v1/preprocessing.py": "62561effccd6856bc404339dddbc8c2daa3a1d0b011be59edd8fd3435802bc30", + "benchmarks/v1/process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A6": { + "application": "A6", + "dataset": "parkinsons", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "0750282946e2e2e5636117db6564652c504b1958ff363c20474816dd3a849f53" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "cbd0f8b308a4b6bcf1994ccc52fc416753db2c7d0544cdc981755061963cbc48" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "018f27e04c960f316c425def9a9dc425ec9033c8d06f208acba646a0e23145ea" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "45e6fdeb7bdc716d4972005915c979eb699c1c830856a50d2212a05fa3611848" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "92cf249be956646637158b98bfe1808716c93e21333a3baccac4ce065e8d9991" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "5eca1356971580b2aa677ccbc3f8abe69b64c7478cd67f7fd25da1e029f7db25" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "9805269dd0594f6e654f4bc64df8694a02a34ffde8474468e4865df7bce4d1d8" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "4947fd9a0515b4646b9638256cf7bb57adf899762c8212a9c58402b0a3dade7f" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "827a1797ca4d64ec7722f4dbb76d2e874a575366eb12ca4e5c07aef15134066a" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "eb6829963b9cdeb2c70583ef5b38d883a4978c9b185e175f81b3302a405d636f" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "79189bbfde0c2e13fa27728ce8a6ead553065da83ad6d94fadaae6163d453296" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "d58b5d592d0c38ab1c7258046dc84598a57f1d447aca23cc750d99835b76f31a" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "c84b2ba1d2884415b029e050876a16d454f1c50d93f1d5804155ecd426d198e8" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "7a81dc178bd308c3799916ba5265742bcd9ca96383ab13f7d4d4ea415dee1a5b" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "c6a3198bfcde399f9add245ef86bae57d7a69457c46dddb48346c98e79efc979" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "cab04f13105a6e4c630bde542ab425099f4422c7b3dbb7f22e9f75aaac8c5406" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "545a95c32e526be45407754189eb4261559fc1e9fdd3b54bf0c19015da69636d" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "df4b66ebc77833c5df46e730f361f14e7f1728ec05883d22fb3208438f398c46" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "6a9d99374e40349110d4b525dcb6c97bee5cddc849de65b1d00da6b9a35de557" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "f696ba5fed863349a0bc216cfeb903a13e9d54ac44b57135c372dcc8759cb08a" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "b0ae47990cc48c72afc47dfcf2273c3cedc5d606289d0751ee2afa3edc465b67" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "4e0f851b54a87b26273eee75ac8d9c20243ea95324ef0329983ca127e9ae2905" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "b32c3fd08cec7781deb95a7d01057e2d8d2d8b0cd63c8c1368b4f5104a8ed95c" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "1d9081dee48c899212497db8d80630f81dd7d934bea31d00cc5781b92666ba0c" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "150f918c1496d3d6fc8062614ce7b592ee00d68a590464f52c50aefb6833b9cc" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + } + } + } + ], + "source_freeze_sha256": "334cbda3dbd18849914a0069b6203035280cf5a716838b098a93856c4a2140f3", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/0/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.9231058750010561, + "artifacts": { + "model.bin": "025f3f6fcd2b9e78e3c3511a855471bc4c773feb0e3ca5461799badaa3d1da12", + "training.json": "a7b457719d1818dd97b2c2f8208b3923c9575fd8afa22c0b2fa0f9cd3d976667", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "a11e3dce279378670b2cb626f44c872a75f722c15387425a2153df70d53ab5ce" + }, + "application": "A6", + "fold": 0, + "job": { + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 0, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-multi-worker-045-final/A6/0/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-multi-worker-045-final/A6/0/fit/model.bin", + "/private/tmp/openboost-multi-worker-045-final/A6/0/fit/features.npz", + "/private/tmp/openboost-multi-worker-045-final/A6/0/fit/replay.npz" + ], + "prediction_shape": [ + 1151, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.0140254110026818, + "last_score": 1.0203684285829633, + "completed_rounds": 4, + "stale_rounds": 1, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "15d2bb99b467704780a70bdacccb031c65b634e6ac6080b0db8b04a6406afe3f", + "best_validation_score": 1.0140254110026818, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" + }, + "replay_sha256": "a11e3dce279378670b2cb626f44c872a75f722c15387425a2153df70d53ab5ce" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/1/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.913862916000653, + "artifacts": { + "model.bin": "97ab141747fef1f1794d4a832fc130e03070ad13b00309170b766cfc534a8a06", + "training.json": "539c657a4479ccccea143a5727721ca85c61a2cde18cee0cee49eac8ce92f57c", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "bd2b24fc742b886f71ad6fe82676e1e8d8baba63a5b21410aebab450063b6d45" + }, + "application": "A6", + "fold": 1, + "job": { + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 1, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-multi-worker-045-final/A6/1/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-multi-worker-045-final/A6/1/fit/model.bin", + "/private/tmp/openboost-multi-worker-045-final/A6/1/fit/features.npz", + "/private/tmp/openboost-multi-worker-045-final/A6/1/fit/replay.npz" + ], + "prediction_shape": [ + 1097, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.8580941834471312, + "last_score": 0.8921598747373427, + "completed_rounds": 4, + "stale_rounds": 3, + "reason": "patience" + }, + "accepted_commits": 4, + "selected_model_identity": "dd8151781709409be307bfbf17c1cd006c4d358b281e5ea80c49d0be970b78a5", + "best_validation_score": 0.8580941834471312, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" + }, + "replay_sha256": "bd2b24fc742b886f71ad6fe82676e1e8d8baba63a5b21410aebab450063b6d45" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/2/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.0311060410022037, + "artifacts": { + "model.bin": "e4cd1bdce695b2ff56d99408b02176f39b2be6e74e848e68bf5f21bbd5d2539d", + "training.json": "5d7920c7094fde7c69674c003a4f231bb58e3a7249818ab6e08c6acf1a37612a", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "34baef0cb200fa5767c59c967247f74efa75b79ed8ca62e768b7c9e41d437b6a" + }, + "application": "A6", + "fold": 2, + "job": { + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 2, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-multi-worker-045-final/A6/2/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-multi-worker-045-final/A6/2/fit/model.bin", + "/private/tmp/openboost-multi-worker-045-final/A6/2/fit/features.npz", + "/private/tmp/openboost-multi-worker-045-final/A6/2/fit/replay.npz" + ], + "prediction_shape": [ + 1062, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.394441268074094, + "last_score": 1.394441268074094, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "71b0d48c094e3fbaf6ba29a610f00f28884491bf358b6681b9527e0d2a2f4472", + "best_validation_score": 1.394441268074094, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" + }, + "replay_sha256": "34baef0cb200fa5767c59c967247f74efa75b79ed8ca62e768b7c9e41d437b6a" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/3/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.4804589999985183, + "artifacts": { + "model.bin": "a512829556d1f79e6929ce52c4775026ae7f1eedecc38549c93c7dd9222c1d81", + "training.json": "39a854f84cdc8058a92256244585573a867a404f223e7e794d03f182ea2cdb17", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "5e790851d06a4df9e9159dea50c0a844332276d1899025bd2390cfbc7a9e53e0" + }, + "application": "A6", + "fold": 3, + "job": { + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 3, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-multi-worker-045-final/A6/3/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-multi-worker-045-final/A6/3/fit/model.bin", + "/private/tmp/openboost-multi-worker-045-final/A6/3/fit/features.npz", + "/private/tmp/openboost-multi-worker-045-final/A6/3/fit/replay.npz" + ], + "prediction_shape": [ + 1129, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 0.693465932244095, + "last_score": 0.7226623309509903, + "completed_rounds": 3, + "stale_rounds": 3, + "reason": "patience" + }, + "accepted_commits": 3, + "selected_model_identity": "9d21a60f7601a2d0b1588f6ccbc9edb76cc91fecf5ddddd4c91ffb5b2f5e3255", + "best_validation_score": 0.693465932244095, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" + }, + "replay_sha256": "5e790851d06a4df9e9159dea50c0a844332276d1899025bd2390cfbc7a9e53e0" + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_worker.py", + "/private/tmp/openboost-multi-worker-045-final/A6/4/job.json" + ], + "timeout_s": 90, + "threads": 1, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.9809298749896698, + "artifacts": { + "model.bin": "1322a9e38866dd9df86971798d9ae14d0dd5edb50feb7445999062acff5ee51d", + "training.json": "1751814014576b20ebc054323277dd5bf7507b1f37034501bf494b3d09c384fa", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "690d01a14687e851078ebbc99063c23469091691b704ed7027f03393f0bdaeff" + }, + "application": "A6", + "fold": 4, + "job": { + "application": "A6", + "library": "openboost", + "device": "cpu", + "threads": 1, + "seed": 4, + "early_stopping_rounds": 3, + "config": { + "rounds": 4, + "learning_rate": 0.1, + "max_depth": 2, + "reg_lambda": 1, + "bins": 32 + }, + "input_npz": "/private/tmp/openboost-multi-worker-045-final/A6/4/worker-input.npz" + }, + "replay_command": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/openboost_predict.py", + "/private/tmp/openboost-multi-worker-045-final/A6/4/fit/model.bin", + "/private/tmp/openboost-multi-worker-045-final/A6/4/fit/features.npz", + "/private/tmp/openboost-multi-worker-045-final/A6/4/fit/replay.npz" + ], + "prediction_shape": [ + 1109, + 2 + ], + "fresh_process_exact": true, + "training": { + "selection": "best_validation", + "stop": { + "rounds": 4, + "patience": 3, + "min_delta": 0.0, + "reference_score": 1.6656270352836269, + "last_score": 1.6656270352836269, + "completed_rounds": 4, + "stale_rounds": 0, + "reason": "budget" + }, + "accepted_commits": 4, + "selected_model_identity": "34a80e1291113ded1ab48b3944f698908b0a717f69b631e66c0e5b2147aed920", + "best_validation_score": 1.6656270352836269, + "output": "multioutput_original_units", + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + }, + "scale_convention": "unweighted_train_population", + "selection_metric": "row_mean_sum_standardized_half_squared_error" + }, + "replay_sha256": "690d01a14687e851078ebbc99063c23469091691b704ed7027f03393f0bdaeff" + } + ] +} diff --git a/benchmarks/v1/evidence/ordered-updates-041/README.md b/benchmarks/v1/evidence/ordered-updates-041/README.md new file mode 100644 index 0000000..0c16e3a --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/README.md @@ -0,0 +1,21 @@ +# Installed ordered-update development evidence + +Parent 6b2385f plus the dirty development sources hashed in manifest.json. +No OpenBoost core source changed. This extends the D2/D3 checks with six D4 cases: +Normal ordinary/Fisher and Formula full GGN, both parameter orders, three rounds. + +- ordered-expected.json: independently generated reference trace supplied to the + installed checker; generator and all reference sources are hashed in the manifest. +- ordered-checks.json: resulting predictions, version counts, maximum reference + difference and the retained unsupported OrderedResult/run_many counterexample. +- ordered-0.json through ordered-5.json: saved raw inference artifacts. +- checks.json, d2.json and d3.json: rerun D2/D3 cases from the preceding sprint. +- manifest.json: exact commands/cwds, versions, source/wheel/artifact hashes and + successful isolated checks plus inference after removing all three plugins. + +Reproduce from the repository with +`uv run --no-sync python examples/v1_extensions/verify.py OUTPUT_DIR`. +Use cached offline build dependencies, Python 3.12 and NumPy 2.3.5. Temporary paths +in the command log describe the actual run; reproduction creates new paths. +These are deterministic CPU development fixtures, not scored E5, external +adoption, complete E2/E6, performance or real-data quality evidence. diff --git a/benchmarks/v1/evidence/ordered-updates-041/checks.json b/benchmarks/v1/evidence/ordered-updates-041/checks.json new file mode 100644 index 0000000..2213353 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/checks.json @@ -0,0 +1,93 @@ +{ + "values": [ + [ + 0.0, + "a" + ], + [ + 1.0, + "b" + ], + [ + 2.0, + null + ], + [ + 3.0, + "a" + ], + [ + null, + "b" + ], + [ + 5.0, + "a" + ] + ], + "row_ids": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "names": [ + "x", + "category" + ], + "kinds": [ + "numeric", + "categorical" + ], + "predictions": { + "d2": [ + [ + 1.5801874999999999 + ], + [ + 1.5801874999999999 + ], + [ + 1.7251249999999998 + ], + [ + 2.106375 + ], + [ + 2.7743 + ], + [ + 2.7743 + ] + ], + "d3": [ + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.164000000000001 + ] + ] + }, + "d2_cuts": [ + 1, + 1, + 1 + ], + "d3_max_absolute_error": 8.881784197001252e-16, + "rounds": 3 +} diff --git a/benchmarks/v1/evidence/ordered-updates-041/d2.json b/benchmarks/v1/evidence/ordered-updates-041/d2.json new file mode 100644 index 0000000..b6d4143 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/d2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [2.25], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.0], [2.0]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [126, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.2874999999999996], [1.5249999999999997]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, 0, -1, -1, -1], "threshold": [190, 63, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.008472222222222017], [-1.7332499999999995], [1.718], [-2.410625], [-0.9612499999999998]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/d3.json b/benchmarks/v1/evidence/ordered-updates-041/d3.json new file mode 100644 index 0000000..5efc3b8 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/d3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [4.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.76], [3.76]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/manifest.json b/benchmarks/v1/evidence/ordered-updates-041/manifest.json new file mode 100644 index 0000000..92d7510 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/manifest.json @@ -0,0 +1,200 @@ +{ + "schema": "openboost-v1-development-extensions-v1", + "passed": true, + "claim": "repository-authored D2/D3/D4 installation and correctness only", + "commit": "6b2385f760db78170cd69f52fe11b9662f75dea9", + "dirty": true, + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "threads": 1, + "commands": [ + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/cohort_splits" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/penalized_leaves" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/ordered_updates" + }, + { + "argv": [ + "uv", + "venv", + "--python", + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + }, + { + "argv": [ + "uv", + "pip", + "install", + "--offline", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env/bin/python", + "numpy==2.3.5", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels/ob_cohort_splits-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels/ob_ordered_updates-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels/ob_penalized_leaves-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/wheels/openboost-1.0.0.dev0-py3-none-any.whl" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/checks.py", + "/private/tmp/openboost-v1-ordered-041" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + }, + { + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "examples.v1_extensions.ordered_oracle", + "/private/tmp/openboost-v1-ordered-041/ordered-expected.json" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/ordered_checks.py", + "/private/tmp/openboost-v1-ordered-041" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env/bin/python", + "-I", + "-c", + "import json,importlib.metadata as m; print(json.dumps({n:m.version(n) for n in ['openboost','numpy','ob-cohort-splits','ob-penalized-leaves','ob-ordered-updates']}))" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + }, + { + "argv": [ + "uv", + "pip", + "uninstall", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env/bin/python", + "ob-cohort-splits", + "ob-penalized-leaves", + "ob-ordered-updates" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m/core_inference.py", + "/private/tmp/openboost-v1-ordered-041" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-ll7ln47m" + } + ], + "sources": { + "examples/v1_extensions/checks.py": "47556dd3e9b5681d1bddb0ae8e086748fe273ddf39e25ab35b5e6a27d34e985b", + "examples/v1_extensions/cohort_splits/pyproject.toml": "d29f65c7783437a6220ec2dcfc6c4497792e804226c2b8f55d33f25560267245", + "examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py": "013730f77db77255d3be58ce16fe4883e55e4dcea80ac757a681fa5444e82178", + "examples/v1_extensions/core_inference.py": "b116de8066a459000fb4a134a9fa520d6a76dd7228c2fa2091731fa52935ff70", + "examples/v1_extensions/ordered_checks.py": "a1008f6842bf822f60656ed404184e0dd438c95c4ab9c1f20a9be3a4e7a85aec", + "examples/v1_extensions/ordered_oracle.py": "f225d630f3ec84645557b1ccc99839864393a1b7c1032ce55f346d2ff2641fe1", + "examples/v1_extensions/ordered_updates/pyproject.toml": "6e3558622d940f91f6454483b962957d218001575dcce5e46172be7769c2c28e", + "examples/v1_extensions/ordered_updates/src/ob_ordered_updates/__init__.py": "4b71fcfba6ccb207f98f10ed765fe0eaa6f8097e678972efd831927ddc8fdcf2", + "examples/v1_extensions/penalized_leaves/pyproject.toml": "e00f1e32176ca2f5295e233c2421c39f729babfea0d283228896b53fd914e4fe", + "examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py": "54208a6c52fe2acacc539f25428f31e28303a286c50dd5892b403c08e1fa0be2", + "examples/v1_extensions/verify.py": "2c2691e60e35d784369c2b0bbced2a9fb6a46d9774e5f76da5e32cce2ab431fe" + }, + "reference_sources": { + "tests/v1/reference/__init__.py": "a6b57448d0bd41863e7d88c7f79a0429652b399ffa8a45478b948cd38f7c57c7", + "tests/v1/reference/author.py": "155d6b2a02baaed5357a1032227e263a6c53d5ee59e6cd6c700f24b13d487dac", + "tests/v1/reference/classification.py": "f3a9cfd5610cef1e2b05cf8e4b2eaf0b77eee5ee12d57d8bdf1c0195613900ad", + "tests/v1/reference/coupled.py": "691c9bd8dacc645d824d7c75bd909590095b07844b923a6929ac79bb8f7e5a8b", + "tests/v1/reference/data.py": "10e4380b35fa156cc6a1c025bca172733289c032b6c4c06421e11e9d430be072", + "tests/v1/reference/integration.py": "62ba6d983b5b457690272d4e28e5b179f6732e0a78ab9faa3ea176fa9119d27e", + "tests/v1/reference/mixed.py": "70dfce643f1b4b6e8d6014c642efbae0d10d32b7269706867af2c590ae84641e", + "tests/v1/reference/positive.py": "d55c40f5dfd96aa58e4ccdb02cb9f7691352e82966bccb4e13d79e278c6ba190", + "tests/v1/reference/quantile.py": "6e05e309c06494cde68d4c11496b7d7aad7f0c4958e0688562fc79451855079a", + "tests/v1/reference/ranking.py": "a2d23b4ba1595b8aac90cdc32b0e41fa90fc42e11a999019d53405eb62199a0c", + "tests/v1/reference/runs.py": "010c43f3752bd22d4959ec89ea40de8f7705ec116dfb53ccae7a3e85c3c93c20", + "tests/v1/reference/scalar.py": "5d78f361abb2bdad2532e322f2b2138ee7c5bd6fed55d6106a63ba707cadb6b1", + "tests/v1/reference/survival.py": "1b4d6704edc2b58c2ddb39b6e4f00de9d205c54250c2a95a8a0458cdab01f46a", + "tests/v1/reference/tree.py": "d8afa1a3ea9447f2fa420317e972e5b2e42540888ce42106d69ccd61aae21874", + "tests/v1/reference/vector.py": "1294d8c452df385c0c31f15c9dd57e11c09ae17bef62d0fa8f5754c4e7efe12f" + }, + "wheels": { + "ob_cohort_splits-0.1.0-py3-none-any.whl": "2e9ebb56cd551f6ebeb150e866c5107f508d62069c45841ffdf0e51c6305141b", + "ob_ordered_updates-0.1.0-py3-none-any.whl": "9a058e89931a5c820350f6b34ccc541c0be86e0becc06d1c14a7b1f943b6c310", + "ob_penalized_leaves-0.1.0-py3-none-any.whl": "e7723fc496bcaf908b52f6ed11483833bbdb1f51da7c5e02eead10d27ed90236", + "openboost-1.0.0.dev0-py3-none-any.whl": "efc7705eb2cde914e4f1445cb087904bee02552df369b270728d7f80f4f7ec01" + }, + "versions": { + "openboost": "1.0.0.dev0", + "numpy": "2.3.5", + "ob-cohort-splits": "0.1.0", + "ob-penalized-leaves": "0.1.0", + "ob-ordered-updates": "0.1.0" + }, + "plugin_free_inference": true, + "artifacts": { + "checks.json": "184006082086b9b5593ea7a7e18c9fdbd34ecae7beb52fa72ac64b883ffd045c", + "d2.json": "facc6da0ae50d74322f4552cf70a11c749f480a7dfb7d102579cec825e88e657", + "d3.json": "f701c628ebbf191056b72bb2a1d3b92d33385189423fa53bc609b33107f803f2", + "ordered-0.json": "325017af3b8f819664eff01df2ac49cfeadc7b042bba416cdd378c3b30016a04", + "ordered-1.json": "03cc3d28f8adc851a28f9596af22376026d8b81b3cce5a126f3ed1c2ea74576e", + "ordered-2.json": "d8b26886a41ee3b194c6983fc2f8b905b741133ef228ffa28fe8f604fd6bba2f", + "ordered-3.json": "35a29588374bfb82a559c42c3a91b67b8fed43a1778610c6078b1c1eda2103d9", + "ordered-4.json": "7ca0acb133c1c034f67f116b58cd4c836ae8c8e67ba31d9656d15c01d90c9de9", + "ordered-5.json": "22067f5ed6ead94e721bf03d652edbe9b3faa3c8c62f08ce85b8f57d12079ac2", + "ordered-checks.json": "da9192dd846f46dc92225b1126b78ac8ecb6e6fbd0bb5aa232e86904f4ea9ffd", + "ordered-expected.json": "68f858fd4edcc561b16c492b146f3f28cc079212797cff6334493893cd499926" + } +} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-0.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-0.json new file mode 100644 index 0000000..3f722f0 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-0.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.0], [-1.55], [1.55], [-1.734375], [-0.40625]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.05211814894448784], [0.537839591477717], [-0.2975117307047917]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00656250000000015], [-1.4378125000000002], [1.4259999999999997], [-1.6042968750000002], [-0.38593750000000004], [2.01625], [0.7743749999999998]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.11905690030262675], [0.37209581591266455], [-0.3125427929230907]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0029869791666665454], [-1.3338359375], [1.3392124999999997], [-1.483974609375], [-0.36664062500000005], [1.9154375], [0.7162968749999997]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.1674487489868146], [0.2577416257105913], [-0.3257519454304436]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-1.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-1.json new file mode 100644 index 0000000..7e2823f --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-1.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-2.4671622769447922e-17], [0.617670054730258], [-0.26471573774153917], [-0.11415168100078188], [-0.30211102423768577]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.0], [-1.55], [1.55], [-1.734375], [-0.40624999999999994]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.08185660294329283], [0.4366019642073911], [-0.29235933130168695]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00656250000000015], [-1.4378125000000002], [1.4259999999999997], [-1.6042968750000002], [-0.38593750000000004], [2.01625], [0.7743749999999998]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.1386300959880271], [0.30287104064812337], [-0.3080405694052306]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0029869791666665454], [-1.3338359375], [1.3392124999999997], [-1.483974609375], [-0.36664062500000005], [1.9154375000000001], [0.7162968749999996]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-2.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-2.json new file mode 100644 index 0000000..208f9e2 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[1.2335811384723961e-17], [-0.31024237685691947], [0.31024237685691947], [-0.3471462079749804], [-0.08131352619233777]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.021532890322055052], [1.202805041760208], [-0.5431730197398742]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.006713870020361525], [-0.29772120406858105], [0.2856362380319303], [-0.3272827548559434], [-0.08973750045956581]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.14554489709520965], [0.7794770933518844], [-0.5211907648446487], [-0.2136889306782752], [-0.601453712375543]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.004724115047481874], [-0.28996494829505315], [0.2814615412095858]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.2124232464137005], [0.551922150051749], [-0.5096536668397931], [-0.2094904804283043], [-0.5878208453187278]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-3.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-3.json new file mode 100644 index 0000000..c6c645b --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-4.9343245538895844e-17], [1.235340109460516], [-0.5294314754830783], [-0.22830336200156376], [-0.6042220484753715]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0001705718713431972], [-0.2921712432158142], [0.29186421384739647]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.13678840055064134], [0.7962304698465822], [-0.5171124306422169], [-0.2224970798353251], [-0.5904591549979086]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.002177683397250046], [-0.28361181227925364], [0.28753164239430373]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.20521263860862954], [0.5620408713757129], [-0.5047194802292578], [-0.21693892691354133], [-0.5764439161728362]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.005841160761291825], [-0.2812088257653382], [0.29172291513566345]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-4.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-4.json new file mode 100644 index 0000000..27d6836 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-4.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.7901594196053434, 0.5413248546129181], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.7132126139527316], [-0.05361965178016352], [1.3374023568950804], [-0.09977238181478348], [0.06549563417915817]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5681877339281609], [-0.10366525878116846], [1.1264031798518581], [-0.16860988364095933], [0.07805662032899752]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6916677838393316], [-0.04950491988957833], [1.2945069308003752], [-0.0932129638142678], [0.06266362790458978]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5002017662617205], [-0.09741553384741679], [0.9977787131185136], [-0.1589450731540854], [0.07435131168962882]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6664451940126678], [-0.045701998802632156], [1.245303348025434], [-0.0871011118862839], [0.05994722676598742]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.44007587706961016], [-0.09152583965232537], [0.8836624183776237], [-0.14982120030930768], [0.07082780148780192]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-5.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-5.json new file mode 100644 index 0000000..6bb1eaa --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-5.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.7901594196053434, 0.5413248546129181], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5925937328319512], [-0.10515689902826628], [1.1718256181257787], [-0.17122868934608548], [0.07956513112150526]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.742570059559497], [-0.05001600877035171], [1.386642115977446], [-0.09451013935344513], [0.063980256781011]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5195503218044729], [-0.09881503450919896], [1.0340056137572504], [-0.16141157957113175], [0.0757855728692661]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.7136045762384516], [-0.046162405522191106], [1.330650642751404], [-0.08830777468338843], [0.0612095355612991]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.4554821173230488], [-0.09283829703588686], [0.9127061082173746], [-0.15214372865535286], [0.07219171472098855]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6819662495495865], [-0.04260017510397546], [1.2701394242932311], [-0.08252636533256741], [0.05855229290519618]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-checks.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-checks.json new file mode 100644 index 0000000..b4a2a29 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-checks.json @@ -0,0 +1,190 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "predictions": { + "ordered-0": [ + [ + 3.3302353515625, + 0.9210958818597373 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.6966171875000002, + 0.7107475316438076 + ], + [ + 4.1165671875, + 0.7107475316438076 + ], + [ + 4.36066875, + 0.9210958818597373 + ] + ], + "ordered-1": [ + [ + 3.3302353515625, + 0.9400424845082175 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.6966171875000002, + 0.7140770860551798 + ], + [ + 4.1165671875, + 0.7140770860551798 + ], + [ + 4.36066875, + 0.9400424845082175 + ] + ], + "ordered-2": [ + [ + 3.7160606088874024, + 1.0577486070660242 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7663984025053043, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 1.0577486070660242 + ] + ], + "ordered-3": [ + [ + 3.726800811873959, + 1.0636893236179212 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 1.0636893236179212 + ] + ], + "ordered-4": [ + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.8089700684903174, + 0.563648427963561 + ], + [ + 4.177880683177432, + 0.8421092857477177 + ], + [ + 4.177880683177432, + 0.8421092857477177 + ] + ], + "ordered-5": [ + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.8085336281300934, + 0.564079096484094 + ], + [ + 4.188902637907551, + 0.8531785886229586 + ], + [ + 4.188902637907551, + 0.8531785886229586 + ] + ] + }, + "versions": { + "ordered-0": 6, + "ordered-1": 6, + "ordered-2": 6, + "ordered-3": 6, + "ordered-4": 6, + "ordered-5": 6 + }, + "max_absolute_error": 2.220446049250313e-16, + "scheduler_status": "unsupported OrderedResult; D5 follow-up" +} diff --git a/benchmarks/v1/evidence/ordered-updates-041/ordered-expected.json b/benchmarks/v1/evidence/ordered-updates-041/ordered-expected.json new file mode 100644 index 0000000..12e7839 --- /dev/null +++ b/benchmarks/v1/evidence/ordered-updates-041/ordered-expected.json @@ -0,0 +1,2232 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "target": [ + 0.5, + 1.0, + 2.0, + 3.0, + 5.0, + 8.0 + ], + "weight": [ + 1, + 0, + 2, + 1, + 3, + 1 + ], + "structure": [ + 0.2, + 0.56, + 0.9199999999999999, + 1.28, + 1.64, + 2.0 + ], + "records": [ + { + "family": "normal", + "mode": "natural", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.771875, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.771875, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8581121376974118 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.771875, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.8581121376974118 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.6390625, + 0.8581121376974118 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.771875, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.8581121376974118 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.8581121376974118 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.73328125, + 0.774577005479161 + ], + [ + 4.0449375000000005, + 0.774577005479161 + ], + [ + 4.169125, + 0.8581121376974118 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.4786328125, + 0.8581121376974118 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.73328125, + 0.774577005479161 + ], + [ + 4.0449375000000005, + 0.774577005479161 + ], + [ + 4.169125, + 0.8581121376974118 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.8953217192886782 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.73328125, + 0.7433227261868519 + ], + [ + 4.0449375000000005, + 0.7433227261868519 + ], + [ + 4.169125, + 0.8953217192886782 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.4786328125, + 0.8953217192886782 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.73328125, + 0.7433227261868519 + ], + [ + 4.0449375000000005, + 0.7433227261868519 + ], + [ + 4.169125, + 0.8953217192886782 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.8953217192886782 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.6966171875000002, + 0.7433227261868519 + ], + [ + 4.1165671875, + 0.7433227261868519 + ], + [ + 4.36066875, + 0.8953217192886782 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.3302353515625, + 0.8953217192886782 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.6966171875000002, + 0.7433227261868519 + ], + [ + 4.1165671875, + 0.7433227261868519 + ], + [ + 4.36066875, + 0.8953217192886782 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.9210958818597373 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.6966171875000002, + 0.7107475316438076 + ], + [ + 4.1165671875, + 0.7107475316438076 + ], + [ + 4.36066875, + 0.9210958818597373 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "natural", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.8125, + 0.8660951840226659 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.8660951840226659 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8660951840226659 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.8660951840226659 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8660951840226659 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.771875, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.8660951840226659 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.6390625, + 0.8660951840226659 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.771875, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.8660951840226659 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.9097553804434051 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.771875, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.9097553804434051 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.6390625, + 0.9097553804434051 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.771875, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.9097553804434051 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.9097553804434051 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.73328125, + 0.7448811429957028 + ], + [ + 4.0449375000000005, + 0.7448811429957028 + ], + [ + 4.169125, + 0.9097553804434051 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.4786328125, + 0.9097553804434051 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.73328125, + 0.7448811429957028 + ], + [ + 4.0449375000000005, + 0.7448811429957028 + ], + [ + 4.169125, + 0.9097553804434051 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.9400424845082175 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.73328125, + 0.7140770860551798 + ], + [ + 4.0449375000000005, + 0.7140770860551798 + ], + [ + 4.169125, + 0.9400424845082175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.4786328125, + 0.9400424845082175 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.73328125, + 0.7140770860551798 + ], + [ + 4.0449375000000005, + 0.7140770860551798 + ], + [ + 4.169125, + 0.9400424845082175 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.9400424845082175 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.6966171875000002, + 0.7140770860551798 + ], + [ + 4.1165671875, + 0.7140770860551798 + ], + [ + 4.36066875, + 0.9400424845082175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "ordinary", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.804368647380766, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.804368647380766, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.777785379202502, + 0.924608682725661 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.804368647380766, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.924608682725661 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.777785379202502, + 0.924608682725661 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.804368647380766, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.924608682725661 + ] + ], + "raw_after": [ + [ + 3.7450571037169076, + 0.924608682725661 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7953948973348095, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.924608682725661 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7450571037169076, + 0.924608682725661 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7953948973348095, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.924608682725661 + ] + ], + "raw_after": [ + [ + 3.7450571037169076, + 1.0025563920608493 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7953948973348095, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 1.0025563920608493 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7450571037169076, + 1.0025563920608493 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7953948973348095, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 1.0025563920608493 + ] + ], + "raw_after": [ + [ + 3.7160606088874024, + 1.0025563920608493 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7663984025053043, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 1.0025563920608493 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7160606088874024, + 1.0025563920608493 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7663984025053043, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 1.0025563920608493 + ] + ], + "raw_after": [ + [ + 3.7160606088874024, + 1.0577486070660242 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7663984025053043, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 1.0577486070660242 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "ordinary", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.8125, + 0.9278621894956918 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.9278621894956918 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.9278621894956918 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.9278621894956918 + ] + ], + "raw_after": [ + [ + 3.7832828756784185, + 0.9278621894956918 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.9278621894956918 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7832828756784185, + 0.9278621894956918 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.9278621894956918 + ] + ], + "raw_after": [ + [ + 3.7832828756784185, + 1.00748523648035 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 1.00748523648035 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7832828756784185, + 1.00748523648035 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 1.00748523648035 + ] + ], + "raw_after": [ + [ + 3.754921694450493, + 1.00748523648035 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 1.00748523648035 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.754921694450493, + 1.00748523648035 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 1.00748523648035 + ] + ], + "raw_after": [ + [ + 3.754921694450493, + 1.0636893236179212 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 1.0636893236179212 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.754921694450493, + 1.0636893236179212 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 1.0636893236179212 + ] + ], + "raw_after": [ + [ + 3.726800811873959, + 1.0636893236179212 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 1.0636893236179212 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "formula", + "mode": "full", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.7967089830232594, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.7967089830232594, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.7967089830232594, + 0.5491305166458179 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.7967089830232594, + 0.5491305166458179 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ] + ], + "raw_after": [ + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.8029753458137185, + 0.5491305166458179 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.8029753458137185, + 0.5491305166458179 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ] + ], + "raw_after": [ + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.8029753458137185, + 0.5565656478147808 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.8029753458137185, + 0.5565656478147808 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ] + ], + "raw_after": [ + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.8089700684903174, + 0.5565656478147808 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.8089700684903174, + 0.5565656478147808 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ] + ], + "raw_after": [ + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.8089700684903174, + 0.563648427963561 + ], + [ + 4.177880683177432, + 0.8421092857477175 + ], + [ + 4.177880683177432, + 0.8421092857477175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "formula", + "mode": "full", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5492813677250686 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5492813677250686 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ] + ], + "raw_after": [ + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.7965574452834443, + 0.5492813677250686 + ], + [ + 3.928823631203088, + 0.658507416425496 + ], + [ + 3.928823631203088, + 0.658507416425496 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.7965574452834443, + 0.5492813677250686 + ], + [ + 3.928823631203088, + 0.658507416425496 + ], + [ + 3.928823631203088, + 0.658507416425496 + ] + ], + "raw_after": [ + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.7965574452834443, + 0.5568599250119952 + ], + [ + 3.928823631203088, + 0.761907977801221 + ], + [ + 3.928823631203088, + 0.761907977801221 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.7965574452834443, + 0.5568599250119952 + ], + [ + 3.928823631203088, + 0.761907977801221 + ], + [ + 3.928823631203088, + 0.761907977801221 + ] + ], + "raw_after": [ + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.802678398839574, + 0.5568599250119952 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.802678398839574, + 0.5568599250119952 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ] + ], + "raw_after": [ + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.802678398839574, + 0.564079096484094 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.802678398839574, + 0.564079096484094 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ] + ], + "raw_after": [ + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.8085336281300934, + 0.564079096484094 + ], + [ + 4.188902637907551, + 0.8531785886229585 + ], + [ + 4.188902637907551, + 0.8531785886229585 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + } + ] +} diff --git a/benchmarks/v1/evidence/parametric-cpu.json b/benchmarks/v1/evidence/parametric-cpu.json new file mode 100644 index 0000000..c12d0d8 --- /dev/null +++ b/benchmarks/v1/evidence/parametric-cpu.json @@ -0,0 +1,99 @@ +{ + "scope": "synthetic CPU parametric controls, not real A9/A12 quality", + "cells": [ + { + "task": "A7", + "status": "pass", + "expected_mean": 0.5384615384615384, + "actual": [ + 0.5384615384615384, + 0.5384615384615384, + 0.5384615384615384 + ] + }, + { + "task": "A8", + "status": "pass", + "expected_mean": 27.142857142857142, + "actual": [ + 27.14285714285714, + 27.14285714285714, + 27.14285714285714 + ] + }, + { + "task": "A9", + "status": "pass", + "expected_mean": 14.615384615384615, + "actual": [ + 14.615384615384615, + 14.615384615384615, + 14.615384615384615 + ] + }, + { + "task": "A9 paid-count times severity", + "status": "pass", + "expected_annualized": 14.615384615384615, + "prediction": [ + 14.615384615384613, + 14.615384615384613, + 14.615384615384613 + ] + }, + { + "task": "A12 global formula", + "status": "pass", + "parameters": { + "amplitude": 3.0, + "rate": 0.7, + "nfev": 6, + "train_age_min": 0.1, + "train_age_max": 4.0 + } + } + ], + "source_sha": "1f29c1c864b9d5eecdbf67f416447aad937ded4c", + "dirty": true, + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "source_hashes": { + "parametric.py": "dabd55ae9e0fb9cb2a0b877277255d96555277ced1bf6f62d89e5ce2eb5b1ebb", + "parametric_smoke.py": "ab8b0842d1d81f668e8526960dc5be6828920e8c80772000fa3dd8b70b12ada7" + } +} diff --git a/benchmarks/v1/evidence/parametric-worker-cpu.json b/benchmarks/v1/evidence/parametric-worker-cpu.json new file mode 100644 index 0000000..50f740f --- /dev/null +++ b/benchmarks/v1/evidence/parametric-worker-cpu.json @@ -0,0 +1,87 @@ +{ + "scope": "synthetic CPU worker contract checks only", + "cells": [ + { + "task": "A7", + "method": "glm", + "status": "pass", + "prediction": [ + 1.0769230769230769, + 2.1538461538461537, + 3.230769230769231 + ], + "expected": [ + 1.0769230769230769, + 2.1538461538461537, + 3.230769230769231 + ] + }, + { + "task": "A8", + "method": "glm", + "status": "pass", + "prediction": [ + 27.14285714285714, + 27.14285714285714, + 27.14285714285714 + ], + "expected": [ + 27.142857142857142, + 27.142857142857142, + 27.142857142857142 + ] + }, + { + "task": "A9", + "method": "glm", + "status": "pass", + "prediction": [ + 14.615384615384615, + 14.615384615384615, + 14.615384615384615 + ], + "expected": [ + 14.615384615384615, + 14.615384615384615, + 14.615384615384615 + ] + }, + { + "task": "A9", + "method": "paid_composition", + "status": "pass", + "prediction": [ + 14.615384615384613, + 14.615384615384613, + 14.615384615384613 + ], + "expected": [ + 14.615384615384615, + 14.615384615384615, + 14.615384615384615 + ] + }, + { + "task": "A12", + "method": "formula_global", + "status": "pass", + "prediction": [ + 0.39192529380358254, + 1.5102440886257713, + 2.632630715241054 + ], + "expected": [ + 0.39192529380358254, + 1.5102440886257713, + 2.632630715241054 + ] + } + ], + "source_sha": "1f29c1c864b9d5eecdbf67f416447aad937ded4c", + "dirty": true, + "source_hashes": { + "parametric.py": "dabd55ae9e0fb9cb2a0b877277255d96555277ced1bf6f62d89e5ce2eb5b1ebb", + "parametric_worker.py": "28cdc0acacf4c44ed1f388e2917a11535c0a0dca44b56c50a5df1d3bb4303fc4", + "parametric_worker_smoke.py": "b0cb47f15a284ad9a7a0e5001d0c8e1c8141ce0d6e39faf602d72a80c0ac7332" + } +} diff --git a/benchmarks/v1/evidence/pyboost-cuda-initial.json b/benchmarks/v1/evidence/pyboost-cuda-initial.json new file mode 100644 index 0000000..813f1a6 --- /dev/null +++ b/benchmarks/v1/evidence/pyboost-cuda-initial.json @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "outputs": 1, + "reason": "Traceback (most recent call last):\n File \"/Users/jiaruixu/work_space/openboost/benchmarks/v1/modal_preflight.py\", line 172, in pyboost_job\n File \"/usr/local/lib/python3.12/site-packages/py_boost/gpu/boosting.py\", line 267, in fit\n self._fit(builder, build_info)\n File \"/usr/local/lib/python3.12/site-packages/py_boost/gpu/boosting.py\", line 236, in _fit\n if self.callbacks.after_iteration(build_info):\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/site-packages/py_boost/callbacks/callback.py\", line 118, in after_iteration\n stop = stop or callback.after_iteration(build_info)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/site-packages/py_boost/callbacks/callback.py\", line 198, in after_iteration\n if ((num_iter % self.verbose) == 0) or (num_iter == (self.ntrees - 1)):\n ~~~~~~~~~^~~~~~~~~~~~~~\nZeroDivisionError: integer modulo by zero\n", + "status": "error" + }, + { + "outputs": 2, + "reason": "Traceback (most recent call last):\n File \"/Users/jiaruixu/work_space/openboost/benchmarks/v1/modal_preflight.py\", line 172, in pyboost_job\n File \"/usr/local/lib/python3.12/site-packages/py_boost/gpu/boosting.py\", line 267, in fit\n self._fit(builder, build_info)\n File \"/usr/local/lib/python3.12/site-packages/py_boost/gpu/boosting.py\", line 236, in _fit\n if self.callbacks.after_iteration(build_info):\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/site-packages/py_boost/callbacks/callback.py\", line 118, in after_iteration\n stop = stop or callback.after_iteration(build_info)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/usr/local/lib/python3.12/site-packages/py_boost/callbacks/callback.py\", line 198, in after_iteration\n if ((num_iter % self.verbose) == 0) or (num_iter == (self.ntrees - 1)):\n ~~~~~~~~~^~~~~~~~~~~~~~\nZeroDivisionError: integer modulo by zero\n", + "status": "error" + } + ], + "cuda_runtime": 12090, + "dirty": true, + "harness_sha256": "f7a1416e60385c5cfb74b7099dda124d57345e46afe9a7bda7445c4554ca0b9e", + "lock_sha256": "8a34f52cbd747faf6c496ce7981f23dc3652c36b115c935f8128265662289801", + "pyboost_path": "py_boost", + "scope": "weighted scalar/vector GPU fit and JSON reload only", + "source_sha": "6f144d270648e03b7bad2d66db7334bfaacbc04c" +} diff --git a/benchmarks/v1/evidence/pyboost-cuda.json b/benchmarks/v1/evidence/pyboost-cuda.json new file mode 100644 index 0000000..ec2785d --- /dev/null +++ b/benchmarks/v1/evidence/pyboost-cuda.json @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "outputs": 1, + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "outputs": 2, + "reload_max_abs_error": 0.0, + "status": "pass" + } + ], + "cuda_runtime": 12090, + "dirty": true, + "harness_sha256": "71202c6b72062a5518ff3e9104d6b2d945e8521b315134bbcb938f1faf721eb4", + "lock_sha256": "8a34f52cbd747faf6c496ce7981f23dc3652c36b115c935f8128265662289801", + "pyboost_path": "py_boost", + "scope": "weighted scalar/vector GPU fit and JSON reload only", + "source_sha": "6f144d270648e03b7bad2d66db7334bfaacbc04c" +} diff --git a/benchmarks/v1/evidence/pyboost-initial-harness-source.txt b/benchmarks/v1/evidence/pyboost-initial-harness-source.txt new file mode 100644 index 0000000..dea7715 --- /dev/null +++ b/benchmarks/v1/evidence/pyboost-initial-harness-source.txt @@ -0,0 +1,214 @@ +"""Allowlisted comparator preflight; uploads no production source or datasets.""" + +import hashlib +import json +import subprocess +from pathlib import Path + +import modal + +ROOT = Path(__file__).resolve().parents[2] +SOURCE = Path(__file__).with_name("capability_smoke.py") +LOCK = Path(__file__).with_name("requirements-cuda.txt") +SOURCE_SHA = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip() +DIRTY = bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=ROOT)) +HASH = hashlib.sha256(SOURCE.read_bytes()).hexdigest() +LOCK_HASH = hashlib.sha256(LOCK.read_bytes()).hexdigest() +app = modal.App("openboost-v1-comparator-preflight") +image = ( + modal.Image.from_registry( + "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + add_python="3.12", + ) + .uv_pip_install(requirements=[str(LOCK)], extra_options="--require-hashes", uv_version="0.12.1") + .add_local_file(SOURCE, "/opt/capability_smoke.py", copy=True) + .env({"OMP_NUM_THREADS": "2", "OPENBLAS_NUM_THREADS": "2"}) +) + + +# The standard wheel lacks CUDA: rebuild the same hash-locked release explicitly. +image = ( + image.apt_install("build-essential", "libboost-dev") + .env({"CC": "gcc", "CXX": "g++", "CMAKE_BUILD_PARALLEL_LEVEL": "2"}) + .uv_pip_install( + requirements=[str(LOCK)], + extra_options="--require-hashes --reinstall-package lightgbm --no-binary lightgbm --config-settings=cmake.define.USE_CUDA=ON --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=75", + uv_version="0.12.1", + ) +) + + +@app.function( + image=image, + gpu="T4", + cpu=2, + memory=8192, + timeout=1800, + retries=0, + max_containers=1, + serialized=True, + include_source=False, +) +def preflight(): + import json + import subprocess + import sys + import tempfile + from pathlib import Path + + import cupy as cp + + results = {} + code = "import importlib.util,json,sys; spec=importlib.util.spec_from_file_location('smoke','/opt/capability_smoke.py'); m=importlib.util.module_from_spec(spec); spec.loader.exec_module(m); r=m.run(*sys.argv[1:4]); open(sys.argv[4],'w').write(json.dumps(r))" + for device in ["cpu", "cuda"]: + cells = [] + environment = {} + for library in ["xgboost", "lightgbm", "catboost"]: + for application in [f"A{i}" for i in range(1, 12)]: + with tempfile.TemporaryDirectory() as temp: + output = Path(temp) / "result.json" + try: + worker = subprocess.run( + [sys.executable, "-c", code, device, library, application, str(output)], + capture_output=True, + text=True, + timeout=90, + ) + if worker.returncode != 0 or not output.exists(): + cells.append( + { + "library": library, + "application": application, + "device": device, + "status": "error", + "exit_code": worker.returncode, + "reason": "native/worker failure", + "log": worker.stdout + worker.stderr, + } + ) + else: + payload = json.loads(output.read_text()) + environment = payload["environment"] + for cell in payload["cells"]: + cell["worker_exit_code"] = worker.returncode + cell["worker_log"] = worker.stdout + worker.stderr + cells.append(cell) + except subprocess.TimeoutExpired: + cells.append( + { + "library": library, + "application": application, + "device": device, + "status": "timeout", + "reason": "90-second per-cell wall limit", + } + ) + print(device, library, application, cells[-1]["status"], flush=True) + results[device] = {"cells": cells, "environment": environment} + results.update( + gpu_inventory=subprocess.check_output( + ["nvidia-smi", "--query-gpu=name,driver_version,memory.total", "--format=csv,noheader"], + text=True, + ), + cuda_runtime=cp.cuda.runtime.runtimeGetVersion(), + cuda_driver=cp.cuda.runtime.driverGetVersion(), + ) + return results + + +@app.local_entrypoint() +def main(): + result = preflight.remote() + result.update( + source_sha=SOURCE_SHA, + dirty=DIRTY, + source_file_sha256=HASH, + lock_sha256=LOCK_HASH, + scope="comparator capability preflight only", + ) + path = ROOT / "benchmarks/v1/evidence/modal-capabilities-isolated.json" + path.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + for device in ["cpu", "cuda"]: + print( + device, [(c["library"], c["application"], c["status"]) for c in result[device]["cells"]] + ) + if any( + c["status"] in {"error", "timeout"} for d in ["cpu", "cuda"] for c in result[d]["cells"] + ): + raise RuntimeError("preflight contains failed cells; inspect saved results") + + +@app.function( + image=image, + gpu="T4", + cpu=2, + memory=8192, + timeout=600, + retries=0, + max_containers=1, + serialized=True, + include_source=False, +) +def pyboost_job(): + import tempfile + import traceback + from pathlib import Path + + import cupy as cp + import numpy as np + import py_boost + from py_boost import GradientBoosting + + rng = np.random.default_rng(73) + x = rng.normal(size=(96, 5)).astype(np.float32) + weights = np.linspace(0.5, 2, 96, dtype=np.float32) + cells = [] + for outputs in [1, 2]: + try: + y = np.column_stack([x[:, 0] + i * x[:, 1] for i in range(outputs)]).astype(np.float32) + model = GradientBoosting( + "mse", ntrees=4, lr=0.1, max_depth=2, min_data_in_leaf=2, seed=73, verbose=0 + ) + model.fit(x, y, sample_weight=weights) + before = model.predict(x) + with tempfile.TemporaryDirectory() as temp: + path = str(Path(temp) / "model.json") + model.dump(path) + loaded = GradientBoosting("mse") + loaded.load(path) + after = loaded.predict(x) + if before.shape != (96, outputs) or not np.isfinite(before).all(): + raise ValueError("invalid prediction shape/value") + np.testing.assert_allclose(before, after, rtol=1e-6, atol=1e-7) + cells.append( + { + "outputs": outputs, + "status": "pass", + "reload_max_abs_error": float(np.max(np.abs(before - after))), + } + ) + except Exception: + cells.append({"outputs": outputs, "status": "error", "reason": traceback.format_exc()}) + return { + "cells": cells, + "cuda_runtime": cp.cuda.runtime.runtimeGetVersion(), + "pyboost_path": str(Path(py_boost.__file__).parent.name), + "scope": "weighted scalar/vector GPU fit and JSON reload only", + } + + +@app.local_entrypoint() +def pyboost(): + result = pyboost_job.remote() + result.update( + source_sha=SOURCE_SHA, + dirty=DIRTY, + lock_sha256=LOCK_HASH, + harness_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + ) + (ROOT / "benchmarks/v1/evidence/pyboost-cuda.json").write_text( + json.dumps(result, indent=2, sort_keys=True) + "\n" + ) + print(result["cells"]) + if any(c["status"] != "pass" for c in result["cells"]): + raise RuntimeError("Py-Boost preflight failed") diff --git a/benchmarks/v1/evidence/ranking-cpu.json b/benchmarks/v1/evidence/ranking-cpu.json new file mode 100644 index 0000000..e73a518 --- /dev/null +++ b/benchmarks/v1/evidence/ranking-cpu.json @@ -0,0 +1,179 @@ +{ + "scope": "synthetic CPU ranking adapter only; no real A4 quality or CUDA acceptance", + "cells": [ + { + "library": "xgboost", + "status": "pass", + "stopping": [ + { + "selected_rounds": 2, + "history": { + "validation": { + "ndcg@10": [ + 0.8381914247741493, + 0.9423683596651978, + 0.9423683596651978, + 0.9423683596651978, + 0.9423683596651978 + ] + } + } + } + ], + "metrics": { + "ndcg10": 0.9625953417560257, + "zero_idcg_queries": 0.0 + }, + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "status": "pass", + "stopping": [ + { + "selected_rounds": 5, + "history": { + "validation": { + "ndcg@10": [ + 0.8695087247686221, + 0.9385006294032445, + 0.9397985261727335, + 0.9397985261727335, + 0.941028319278816, + 0.941028319278816, + 0.941028319278816, + 0.941028319278816 + ] + } + } + } + ], + "metrics": { + "ndcg10": 0.9615070179604602, + "zero_idcg_queries": 0.0 + }, + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "status": "pass", + "stopping": [ + { + "selected_rounds": 14, + "history": { + "learn": { + "PairLogit": [ + 0.6134591075113905, + 0.5484427352035695, + 0.5175296507286621, + 0.47488529025465126, + 0.445051482549306, + 0.4137688789606885, + 0.3852264273948403, + 0.3724183645850257, + 0.351963655075019, + 0.340325426445951, + 0.33877538470972396, + 0.3201317606081223, + 0.3071798798328893, + 0.2989135598128324, + 0.29831470469662025, + 0.28470892307592394 + ] + }, + "validation": { + "NDCG:top=10;type=Base": [ + 0.8794807098638694, + 0.9286061082129299, + 0.9816429716833202, + 0.9933619881403539, + 0.9939054216546843, + 0.9950744910933638, + 0.9950744910933638, + 0.9950744910933638, + 0.9960278310547466, + 0.9972482251506853, + 0.9984745073800765, + 0.9984745073800765, + 0.9984745073800765, + 0.9990847044280459, + 0.9990847044280459, + 0.9990847044280459 + ], + "PairLogit": [ + 0.617056222476761, + 0.5573675403722873, + 0.5202116712184209, + 0.4757089819107181, + 0.4553245643562779, + 0.4243040788872626, + 0.388006705039589, + 0.3683927746612372, + 0.35199403879322, + 0.34294633850313383, + 0.3427386658623359, + 0.32573460951270783, + 0.31068626242865005, + 0.30504504182789643, + 0.3049335444209221, + 0.292310445603109 + ] + } + } + } + ], + "metrics": { + "ndcg10": 0.9996175349641385, + "zero_idcg_queries": 0.0 + }, + "reload_max_abs_error": 0.0 + } + ], + "seed": 73, + "threads": 2, + "source_sha": "1f29c1c864b9d5eecdbf67f416447aad937ded4c", + "dirty": true, + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "source_hashes": { + "ranking_smoke.py": "1e6233da89f811b12c3f39560e2fa4c154a43d8cc966aaa00ee359fdbbcc6f19", + "ranking.py": "dab78a6a98b26490e3de66d7d8acdae8f6ce3ef94c4c5505bc6ace61176c08bb", + "baseline_worker.py": "5c820bd32b898263c4cdd06ea113a5b7922a06b5c7677212003d9c2563aa0d3e", + "quality.py": "6736b97666f73a0c1831fd5c9472a4fd66cc675d2b66884a684cdad92354e827" + } +} diff --git a/benchmarks/v1/evidence/ranking-smoke-initial-source.txt b/benchmarks/v1/evidence/ranking-smoke-initial-source.txt new file mode 100644 index 0000000..e09ac6e --- /dev/null +++ b/benchmarks/v1/evidence/ranking-smoke-initial-source.txt @@ -0,0 +1,78 @@ +"""Weighted query-aware ranking fit, validation stopping and score reload.""" + +import hashlib +import importlib.metadata +import json +import pickle +import subprocess +from pathlib import Path + +import numpy as np + +from benchmarks.v1.baseline_worker import fit, predict_saved +from benchmarks.v1.quality import metrics + +if __name__ == "__main__": + rng = np.random.default_rng(73) + x = rng.normal(size=(96, 4)) + y = np.tile(np.arange(4), 24) + x[:, 0] = y + rng.normal(size=96) * 0.5 + arrays = dict( + x_train=x[:64], + y_train=y[:64], + x_validation=x[64:], + y_validation=y[64:], + validation_row_ids=np.arange(64, 96), + query_train=np.repeat(np.arange(8), 8), + query_validation=np.repeat(np.arange(8, 12), 8), + query_weight_train=np.linspace(0.5, 2, 8), + query_weight_validation=np.linspace(0.5, 2, 4), + ) + results = [] + for library, cfg in dict( + xgboost=dict(max_depth=2, reg_lambda=1.0), + lightgbm=dict(num_leaves=4, lambda_l2=1.0), + catboost=dict(depth=2, l2_leaf_reg=1.0), + ).items(): + job = dict( + application="A4", + library=library, + seed=73, + threads=2, + device="cpu", + early_stopping_rounds=3, + config=dict(rounds=16, learning_rate=0.1, **cfg), + ) + prediction, saved = fit(job, arrays) + replay = predict_saved(pickle.loads(pickle.dumps(saved)), x[64:]) + np.testing.assert_allclose(prediction, replay, rtol=1e-7, atol=1e-8) + stop = saved["stopping"][0] + vals = list(stop["history"]["validation"].values())[-1] + assert stop["selected_rounds"] == int(np.argmax(vals)) + 1 + scores = metrics( + "A4", y[64:], prediction, query=arrays["query_validation"], row_ids=np.arange(64, 96) + ) + results.append( + dict( + library=library, + status="pass", + stopping=saved["stopping"], + metrics=scores, + reload_max_abs_error=float(np.max(np.abs(prediction - replay))), + ) + ) + result = dict( + scope="synthetic CPU ranking adapter only; no real A4 quality or CUDA acceptance", + cells=results, + seed=73, + threads=2, + source_sha=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + packages={d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + source_hashes={ + n: hashlib.sha256(Path(__file__).with_name(n).read_bytes()).hexdigest() + for n in ["ranking_smoke.py", "ranking.py", "baseline_worker.py", "quality.py"] + }, + ) + Path("benchmarks/v1/evidence/ranking-cpu.json").write_text(json.dumps(result, indent=2) + "\n") + print(results) diff --git a/benchmarks/v1/evidence/ranking-smoke-initial.json b/benchmarks/v1/evidence/ranking-smoke-initial.json new file mode 100644 index 0000000..c02f7ee --- /dev/null +++ b/benchmarks/v1/evidence/ranking-smoke-initial.json @@ -0,0 +1,6 @@ +{ + "status": "error", + "scope": "synthetic adapter smoke", + "reason": "AssertionError comparing selected_rounds with argmax of the last native validation metric; last metric is not necessarily NDCG", + "source_sha256": "29b5ec36691ef33cdea59d641a20e72a889b55d2f9a49b58e8f1e82eeac56bd6" +} diff --git a/benchmarks/v1/evidence/real-classification-quantile-binding-cpu.json b/benchmarks/v1/evidence/real-classification-quantile-binding-cpu.json new file mode 100644 index 0000000..c6e81db --- /dev/null +++ b/benchmarks/v1/evidence/real-classification-quantile-binding-cpu.json @@ -0,0 +1,2759 @@ +{ + "scope": "Real-data validation worker plumbing only; four rounds, no test scores or search selection, no quality or performance claim", + "source_sha": "8427941bee61255fba4ce9c1ea7ca5f9d4e1443f", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "-m", + "benchmarks.v1.worker_data_smoke", + "build/v1-worker-classification-quantile-001" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "device": "cpu", + "gpu": null, + "memory_cap": null, + "threads": 2, + "source_hashes": { + "worker_data.py": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "worker_data_smoke.py": "1b956f34aeb02dcb5d52c8af6054b95e1160390b1466fad96610aeccf4c3a0bb", + "baseline_worker.py": "23a77399fe0d8f22ca8562801e43ea50e6ca492501c2feae278937e8c44b69b0", + "process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A1": { + "application": "A1", + "dataset": "housing", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "ee9fb6d9cbbc8ee3088413ffefb7467c221df5606372269f81b5225087007928" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "610e546bd63e5b7ad2c1be26db958fd00536b6d9e4026f102e29ebfcf003cece" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e4f404f717fa4b6c6fbea5819f4fa572336d4b96fd351c824a27373d78eba473" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "cacb45b11d4ceeb3997895e9bee1398a2bf8a8711fabffdc1dedd96d19e66f10" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "07180ad525f246a21d67918781b061f53b76249f9775e8aa010ad39eaa1b2631" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "9b73a363988ddc95d2b7030ca1b387d22e9e7f44bc30f694058f251f718f4a7e" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "0c5fe3cf4b800fee9346ffc9214815d6f93064b3a0482fc15b78e231d3888102" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "492984cc05178c60813ddcb62e798c4c9aaa075273537b2d5bc7ed5f2cbf438d" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4130a6ee488ec32a4555f3f9f55bbbc5dac85ee4f55b1d2a6d6d0c0bcb35e82f" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "f0d1d8c9180e58156138c18fbfa7e9b10fbba3aac5fcf26054f0c6e84d0fbd37" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bdc4220a156117b1656b4867ef417c1988eb89fa8cf6d8f19a41b17214845305" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "4d267b0afe6968592ea8010a34aa7571922c900d6a2baa264d3ecac8f44021db" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4ab22696f06c57eb49ec3f94788aa5a59e8417c3eaaf1f994b3210a3b34ed327" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "66749ebd46b211b0d8d8f4b9efb458a5ffcd5a3620ed6b0766f5d0e312b8e20a" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "1442212a3dd1bad69b848fef0a9d4746688e614edfe4f99cb84add5c3630a1b1" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3fc154610f4ff93c172802d57afa274c299940c638dacd4869082c8bc5bc3c51" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "a80fd6416a556871e3f57ea5924f6b5f26adca2a55e9f6991495777600b4a2f5" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "98fdf48b758b632e87581c66517e9c9b89e09e46f4fbeca3eb7ccbba3e443e67" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "d52e364a351d6424c29c95d226d3962c9727e9b61acfbd5ec9861bddd3444ed3" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "d02417bd952ef72b6da1865cfac5f7667ef329897eaac3bc817b3dd291ab4369" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "a3cd76073e4a14f666ed41182287b86009dbde10ec7cc0637926aa30d3b46783" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "b798bb52c90d3c9866582b355d3df6cfd690e97fa32cfe5b69cd19d8f9bb6a41" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "677e837444eb3338e6a23a93f2ab693bb5afd1b96c3ea08023af41c4befb29ce" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bbf8716a861ff6d59ce784d7b0e948c43484f8f2226c9f0f601a1ed01195d613" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "f769ecfe75a7cf24ce3ad9ae86132365333baf65c7372de2c8f03960636a790d" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "93a5e050bdf4c3ae0c526fb0a75bd806df404c8ab558e3019bcc0bdcd28448ae", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A2": { + "application": "A2", + "dataset": "adult", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "e04e33fd11559a52e9f899fa332d7291d55569b2e824de8d6ce2858338817092" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "9f45ed87577102e8486a09f1c19bb4067fea4ae8d9aca43283c1cb67707ae832" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "f8e859a818998f2c994b98dedbb0171be1eb0b4c6f54dc6fe305b94d448a53bd" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "2e72125409d9cf1fe27e45dd2b614afb7f9dd02ef17597eb5c6172d00cf8f7cc" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "cb1193a91a859878036a72cc2c1712aa0b5fe24d730519a0f484d6ae166f6278" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "54db181f0f5ab5c7fa90595a308f7584d079c066cc9d1ef4e7118631e27be888" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bee5ad1786121477fd4d262197bc66eee98cefad9eb9662b2f385da02958ce98" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "c57b4786ab885b7da2fd5dc2725adba94c25836501757fce55e8811f45ac489e" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "181f31e71c136cd6335c4077ea29a39dfa47abd3eefed55f4b05db70289cc070" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "6d758560150d299bf28b5b9c0093b742918c17bd6b0f8318af88534249263ee2" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "00bdb2099f2313e3b7655d3ba41f3f5509665e36e6f269e7c74d6ecc89f688b7" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "c5bda1ce25eb633c6d73572b5b8ce5a48b14e0c837fdf6ef2010ad64e2f2cbab" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "035f87ddc1ffda1c5ddd877456ccb79f25c579486f558335467212b1a9c118f8" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "e11c4eb51ff1d7a1fa54924e8cc30d5e562fc775380a2045be8e43317ea42a02" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "f8341f7a2900ac706f37f2eca92745d0a0d771b33fcdfbe61496d2e6ebf52b3d" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "4cd3073289bd5c96fb0ab8a1c6c28f3d952e54f2743d7ae5c4de6e5c18e195cf" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "9c069a597d24983564df74034251a9422b470b22ab856632afc7ad1bb085d4ce" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "6f4eb8e49ee7879149de5ace3c26ea9c6a79d9f9a59c6c18bfb99bccd5747c72", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A3": { + "application": "A3", + "dataset": "covertype", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "9bc25256b6a57efa51f13e714c6b47496239858ac851e97c3d74ebe47db71f1a" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "be5d90d83aa4daede0678e41425ff4ba5c45dbf074938113af0ad17b421a2f6c" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "36dda823586db6a9ddf91fd0d9e2520339d2673c05376469b685b6477f9a766d" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "0b1dff90f6c0f819af4212b7c8f70f4ad81fc601c82d7cfa654cca1a6818f314" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "939fae1e6bb73bd032846d1d746ca878d9d04e632fa44fa167186f62274ce168" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "d64c60a6f43d571eeddc9a9bcbda756b80fe4941f86322908f32e7f4717fad39" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "4a76893b287be4ae6d426237a31964d3ced678a3146b130774e5375925eb23d1" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "28c6c8c65d8a2bea7ae4143cad6779663210cc9f96fb80c7964916df0f1be0d7" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "58cb3d94f756b63b04a367e966f35cc96f89575a7648a8ddfdc8a8557e91e0d8" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "8bca09f723c78f2d5b76b502a9b9c13c32d128d500468eb569ec3e1206ef8d06" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "5714e839b4f64cb5c9aba8bb348126f0f472adffba9370ca4c2c478e8f922766" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "dc1172c948a9a36e8655ae0cd8abfe06a20e36edb95a2dcf848a0fbca56e05b6" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "0008f26455bff9cf865f311e8e12827f4f7ee3a6e130ae8e7cbddf52bff08e22" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "55d1bfe08be7912d5bf6abc393debce5e3256b966fd028b9c58ff25ae105425d" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "93fbfff4e8b6f0d0af1e2545a2880912000bc10b13edf7ca04558405239c5efa" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3925324d0008087680b73df1ce383154ccb06c15b3781c6d614e461b58caa1a4" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "b9bd2397c5589902abd54b030e034a6d15391d08e3ba7b8417b730d255ebb287" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "8dc679559db25ab5b539ea1b497b17f648b873d53918a55908c6510df3ec9728" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "f30c6b3b8b37878446033c7876190aff6bd7338ee53471965776c3f121ab4062" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "a9cc4943bbe5511863e1f44c50e0a6fa842f255acc4ad641553ac63f9efb177b" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "2887c6bba88c012857b33c316dd0f60bf53218d0d966ab1a1a7e1ea1a7fd54df" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "375c27849c96df36923c5d0725e339d1158147330b7b90f88d9f060c6e7152a0" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "ec378b293641f6c2994840ebeb39545d4b13cafff26e4fe3652ac514a01d83a3" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "c8e80caf8655a4cb2cb2f6a259fba3db8f87279c0992514107465f59b5babd69" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "7113772cb35562a039be77a3874cc6f807886f16f51603c2318547170c2f1153" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "a4c9a1614b7e0d4d74d99194f498a0a387f9d547992981b8c3219ca79cd1f675", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A5": { + "application": "A5", + "dataset": "bike", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "edf7c8ba8770d511ebfb30d14868ec7f1c8d5588a1be3ac6145fff9b2debe0aa" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "08c923c33d68f5e2796061a2046670d69ffdfee22a08ecf9c9ce8a67fb1392ee" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "697eb5c26efda90a7bfa4f60e00349154b74ab7cef98897002622d09a0c2ad20" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "6e0ec20f81973cf3f33156d9e2e443c2b14578f7eef0f45cf26f2d4caa27ac97" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "4cab26296e9bf7400f6a4b4e49c02593c9f58bf2132737a6f313ff7601c77d1b" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "4359b667f6259bc5a220ac984e9b1076eefbeebc090bf2c9b5c4e7961a8a3a4c" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "51d99cecf31580385da6465bce242e9c06e022e39dcb72710e420535e8b03a88" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "43d2e9f4a28927502bbeacaabf02aa7daa5da39a219470538d9c19fb8d616271" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "cc7c62b45e9f6ca11b4cb9941c3b2a27ffb213348c72cdc113a818e9e2a91950" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "d137f253a5a19cb11520e03774ea12850b896d6aa051e846490876e38d1e74dc" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "8c9c632cf19063343dabd5de487ca4877718deb584b5ac0bb366255127c814bd" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "7a4cda7d5b570da10f4e75d14a259a8e0357a7b06192f13bbbe037d033999869" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4cab26296e9bf7400f6a4b4e49c02593c9f58bf2132737a6f313ff7601c77d1b" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "7f1e11bce4cc1afeb9708c6e7a9d11166adaeb23292ce60f9d3279e5db515f92" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "7ac77ff0b16720cc6029c8415326311d46351c1eff653ca4a9e8059d3891644b" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "5a23a5c8e394cefe92c5085a9555766cba06bf8239c8d0dbd461463b492904d2" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "9aee6b24ca413c5f932d79e496c3f457a83f0d1ed972e958918007210eb3f765" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "d137f253a5a19cb11520e03774ea12850b896d6aa051e846490876e38d1e74dc" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "ec7e484babfc292708baee9e0295fd8e08f250ac404fd25999a257837b073b25" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "0612b31c43d862cf5b84c0121361e89548900107937760a8a4d8f05f87215ba9" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "f9f683a96223e022fa52352e7e03dab8fb75172e387ff91370c7abcb1f02c1f6" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "5e9da365f4631bca2f9c6e81f7303761b81677873e0b13c2d6280480a0a43a27" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "7ac77ff0b16720cc6029c8415326311d46351c1eff653ca4a9e8059d3891644b" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bff6160e06fc9d031d999617b457471cd736ec42115c411540e797a1bb0a3379" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "0f2c1343f58417dd48447fa542c6464ea74d2b431bb408cce4e1178150b23b8b" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "3a77d0f7d8e903061eb647bf26f2f0de5f05caed7d301ca929039fb4deddb7c0", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A6": { + "application": "A6", + "dataset": "parkinsons", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "0750282946e2e2e5636117db6564652c504b1958ff363c20474816dd3a849f53" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "cbd0f8b308a4b6bcf1994ccc52fc416753db2c7d0544cdc981755061963cbc48" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "018f27e04c960f316c425def9a9dc425ec9033c8d06f208acba646a0e23145ea" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "45e6fdeb7bdc716d4972005915c979eb699c1c830856a50d2212a05fa3611848" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "92cf249be956646637158b98bfe1808716c93e21333a3baccac4ce065e8d9991" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "5eca1356971580b2aa677ccbc3f8abe69b64c7478cd67f7fd25da1e029f7db25" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "9805269dd0594f6e654f4bc64df8694a02a34ffde8474468e4865df7bce4d1d8" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "4947fd9a0515b4646b9638256cf7bb57adf899762c8212a9c58402b0a3dade7f" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "827a1797ca4d64ec7722f4dbb76d2e874a575366eb12ca4e5c07aef15134066a" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "eb6829963b9cdeb2c70583ef5b38d883a4978c9b185e175f81b3302a405d636f" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "79189bbfde0c2e13fa27728ce8a6ead553065da83ad6d94fadaae6163d453296" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "d58b5d592d0c38ab1c7258046dc84598a57f1d447aca23cc750d99835b76f31a" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "c84b2ba1d2884415b029e050876a16d454f1c50d93f1d5804155ecd426d198e8" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "7a81dc178bd308c3799916ba5265742bcd9ca96383ab13f7d4d4ea415dee1a5b" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "c6a3198bfcde399f9add245ef86bae57d7a69457c46dddb48346c98e79efc979" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "cab04f13105a6e4c630bde542ab425099f4422c7b3dbb7f22e9f75aaac8c5406" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "545a95c32e526be45407754189eb4261559fc1e9fdd3b54bf0c19015da69636d" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "df4b66ebc77833c5df46e730f361f14e7f1728ec05883d22fb3208438f398c46" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "6a9d99374e40349110d4b525dcb6c97bee5cddc849de65b1d00da6b9a35de557" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "f696ba5fed863349a0bc216cfeb903a13e9d54ac44b57135c372dcc8759cb08a" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "b0ae47990cc48c72afc47dfcf2273c3cedc5d606289d0751ee2afa3edc465b67" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "4e0f851b54a87b26273eee75ac8d9c20243ea95324ef0329983ca127e9ae2905" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "b32c3fd08cec7781deb95a7d01057e2d8d2d8b0cd63c8c1368b4f5104a8ed95c" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "1d9081dee48c899212497db8d80630f81dd7d934bea31d00cc5781b92666ba0c" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "150f918c1496d3d6fc8062614ce7b592ee00d68a590464f52c50aefb6833b9cc" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + } + } + } + ], + "source_freeze_sha256": "334cbda3dbd18849914a0069b6203035280cf5a716838b098a93856c4a2140f3", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A11": { + "application": "A11", + "dataset": "housing", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "ee9fb6d9cbbc8ee3088413ffefb7467c221df5606372269f81b5225087007928" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "610e546bd63e5b7ad2c1be26db958fd00536b6d9e4026f102e29ebfcf003cece" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e4f404f717fa4b6c6fbea5819f4fa572336d4b96fd351c824a27373d78eba473" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "cacb45b11d4ceeb3997895e9bee1398a2bf8a8711fabffdc1dedd96d19e66f10" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "07180ad525f246a21d67918781b061f53b76249f9775e8aa010ad39eaa1b2631" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "9b73a363988ddc95d2b7030ca1b387d22e9e7f44bc30f694058f251f718f4a7e" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "0c5fe3cf4b800fee9346ffc9214815d6f93064b3a0482fc15b78e231d3888102" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "492984cc05178c60813ddcb62e798c4c9aaa075273537b2d5bc7ed5f2cbf438d" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4130a6ee488ec32a4555f3f9f55bbbc5dac85ee4f55b1d2a6d6d0c0bcb35e82f" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "f0d1d8c9180e58156138c18fbfa7e9b10fbba3aac5fcf26054f0c6e84d0fbd37" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bdc4220a156117b1656b4867ef417c1988eb89fa8cf6d8f19a41b17214845305" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "4d267b0afe6968592ea8010a34aa7571922c900d6a2baa264d3ecac8f44021db" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4ab22696f06c57eb49ec3f94788aa5a59e8417c3eaaf1f994b3210a3b34ed327" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "66749ebd46b211b0d8d8f4b9efb458a5ffcd5a3620ed6b0766f5d0e312b8e20a" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "1442212a3dd1bad69b848fef0a9d4746688e614edfe4f99cb84add5c3630a1b1" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3fc154610f4ff93c172802d57afa274c299940c638dacd4869082c8bc5bc3c51" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "a80fd6416a556871e3f57ea5924f6b5f26adca2a55e9f6991495777600b4a2f5" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "98fdf48b758b632e87581c66517e9c9b89e09e46f4fbeca3eb7ccbba3e443e67" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "d52e364a351d6424c29c95d226d3962c9727e9b61acfbd5ec9861bddd3444ed3" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "d02417bd952ef72b6da1865cfac5f7667ef329897eaac3bc817b3dd291ab4369" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "a3cd76073e4a14f666ed41182287b86009dbde10ec7cc0637926aa30d3b46783" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "b798bb52c90d3c9866582b355d3df6cfd690e97fa32cfe5b69cd19d8f9bb6a41" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "677e837444eb3338e6a23a93f2ab693bb5afd1b96c3ea08023af41c4befb29ce" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bbf8716a861ff6d59ce784d7b0e948c43484f8f2226c9f0f601a1ed01195d613" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "f769ecfe75a7cf24ce3ad9ae86132365333baf65c7372de2c8f03960636a790d" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "93a5e050bdf4c3ae0c526fb0a75bd806df404c8ab558e3019bcc0bdcd28448ae", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A12": { + "application": "A12", + "dataset": "concrete", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "1409cb024293499551527b6bbf635c9d01e97de7731ec170d4e5950b13aea531" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "810fd9c5067134e4de2fe9ab61092908713a2bcb3fc99f9aa86d6835649bf878" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "616c8feb197f7cce51a5c21fa3badb29532f4453d8fb1a212c5e71caa403a969" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "2cf36ce99d30c832c86a07a1cf250d8a992f2c2a6d1cdd3089d220eb42c88534" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "cc6daae2aa8d170f17aa018439cfbbc5e606a9ece3c455a455bf8c1a985e6a3f" + }, + "validation-structure": { + "path": "0/validation-structure.npz", + "sha256": "59ebd7cd5785f96c4f808dcde1ef3709a81b346b6937073bc09f7cb157962a07" + }, + "test-structure": { + "path": "0/test-structure.npz", + "sha256": "05fe7a98b2dc54e8824293afdf17137715ae6d3616919978de9f729a18ebf0d4" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "d17dd325727de736821bf3435ad5051ae36c00b7d4f7efcf99dcb16dacfbc7b4" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "2bfd635778f518a16be87778cc72601cd927d9e34be5e6eda638480ac7947cc9" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "8f79da547abcce2ff7c262cf4f25b54f2ca2fd0af13c3f02b4074af02007ce7c" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "a4612a16d835cd2d712f37484d5ff3db4f0e2a0c22bde74b581d6dc8bf54df64" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "19478323cc0ec2d8bf8378c375c29afae01d1dacae29a40a70666f071e49960a" + }, + "validation-structure": { + "path": "1/validation-structure.npz", + "sha256": "b8e961ee37afa13f939ac261834c328667f1aaf44c8f88db85d8d4b813d96880" + }, + "test-structure": { + "path": "1/test-structure.npz", + "sha256": "1ad63654994b3fdc6aad9e318410b8df8bf5bcd39c04a28db640b4059e7f454b" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "074688f4eb2b2a68e812f8af1768364d0dc2ca08a9962f930076c9d1e8e5d9ef" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "edf5a2d705375a06bdc9ef06e647ea150dc099c2ff44139e7b0675d3a7e0d1a0" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "bb1b0fe4e32d296a92a0d8971aa2ae88d16f766e39868e475c93c7ce50d83aec" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "ca23ff9eea4c59b9acfc2fd36fa9b3404b908fae1a566a9f1d06673b84215478" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "17ad5fce88828052c181bdaf81a8022bc0201fdb7952015bf9df16953cf693ab" + }, + "validation-structure": { + "path": "2/validation-structure.npz", + "sha256": "a9c070ca3d998086e4a02acc40d0953430edb43934db4f649aca2a756bcf6360" + }, + "test-structure": { + "path": "2/test-structure.npz", + "sha256": "c25925aa72084a9b3d9d62285ca793a49bf820333c2f4993734caa4a3fc33302" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "1722d246878ffead019d3d9b94e7343325c9f162c1a565ddd95664d6dd37f959" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "bf38782ee6b21b3748ab91c048be51663bc2517dd4734cee8b9d5932b6a49a7f" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "9b102f96fe81c4177da8c2ab0d0bf17107f80576544dc6337bfe1af978974871" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "9c07a03bd91275e86df11fea4f2c835261a305b626a76a3b10a4621c979ed877" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "3cdf2601b954023779c32ab58401241c201cc0bfb4a3951f99ced2c0dd3609cf" + }, + "validation-structure": { + "path": "3/validation-structure.npz", + "sha256": "7e5be679ee51be580728d0b93086ba61de01c61badb36f8a74f08636dc1e95bf" + }, + "test-structure": { + "path": "3/test-structure.npz", + "sha256": "ada5a1c15447085d3f249c39a17b8df6dd04b2bc507d97dec03cef434c3d5dde" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "c82bdfd8634be906a9717cfe1d17175519311ad4b262c4b5ef9a30ef4b04abce" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "5d8f14f0b1648ea0d8322d6a1114cf1745367f6ba0f17f3268fd68620dc7e370" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "a8a6d3bd7f7844e4829cca0761b8085d3d352b3ef1cba89663cf22577a43d8a2" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "6b266d810547087863bcd3e9c03ab123b549e461f8a4f02cc19ddafea08cab0d" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "d2423a1c9cafa045ea935f6fc952e037402f8c21cb660ab2e0a8ac412d67150f" + }, + "validation-structure": { + "path": "4/validation-structure.npz", + "sha256": "525e5e63ecc36671289b3a60d4b3efecf69f6cdfc203db5dc8b34cc8de26717b" + }, + "test-structure": { + "path": "4/test-structure.npz", + "sha256": "e6a7b82c20849d16220050d3112a342e460a81f12e248216a0b112280c0a6804" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + } + ], + "source_freeze_sha256": "094049cfe8a9137b02a2599e6be823d669f191d31828989a9dae46797ef6e439", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "46aa82eb2c4ed409244e9c6b3b9101866c2bb475e9edccf2bc22ede6496c29d1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A1/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.4916220839950256, + "artifacts": { + "model.bin": "5ecb3b570df366e2f756030ba64456314c72a366c6a3b2b1eabc2dea91c392a4", + "training.json": "3e29092b73a92c2315407cd4fc9ac67e7f6a32994b3c11e908694e71071461e0", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "5e3f564622e1e1a04f6beb989eb2445990225260115d0a0348898cd9d490dd7e" + }, + "application": "A1", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.1040290366770373, + 1.0606887101824045, + 1.0218986555332432, + 0.9879878092935267 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A1/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1039274590002606, + "artifacts": { + "model.bin": "4f1c4b05a02d75be1459ec315e3cb4e87ce69289da854967e17faf0a6acd76d9", + "training.json": "716f5d21daf8b657d16f8938b3e15d165a75b94ca178d61cd51af475be688c66", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "bcb8a0912f960370e31674938e6e4fe96805ed9b9217162c910549c89a7fc0cf" + }, + "application": "A1", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.12648011579946, + 1.082676299088048, + 1.0438404198892604, + 1.0100368469056356 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A1/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1680871669959743, + "artifacts": { + "model.bin": "d6beb9beafae3e50a318ab6b03d4203a7a39cc50872d8e59093fc39af70c88ff", + "training.json": "1dce227f2df0b32b3de1f54ac36ed55615084f217f104b0076343494951d0ae2", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "98334943d25e80cf0f26f6abae07f1ed51927664f01ecf0dcff9adad04fe392c" + }, + "application": "A1", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.1225223244307807, + 1.0803266948728714, + 1.0439130513244994, + 1.0109379748479417 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A1/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1016408750001574, + "artifacts": { + "model.bin": "c418c70a9ae639004bde3a6469ff262d7ce8fe3e2db8a286f0679473ac05975b", + "training.json": "8def85925bec9f9ac860022202007f4c8cda761e54c8a3f67644cb92822a5655", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1a04b2817da59b47cfad89fa171a9bb15a405370b5b27d4f2fa1965babe299d5" + }, + "application": "A1", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.094678299070021, + 1.0522510401996432, + 1.0156013308134624, + 0.9829172497917497 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A1/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1110243750008522, + "artifacts": { + "model.bin": "68871514ec0c9d814f17e9eea99d364c21c2f80b0b3245801bc58f55690efc5e", + "training.json": "9d3a6ccd3732f117ecce3e3223271650d96e3981abb7be512fc502740e04048d", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "072d7f0efc9f4ef07d9927df274abc922937bccc3cb6f4120c139c5a9f314b39" + }, + "application": "A1", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.1138331229959961, + 1.0724223160201456, + 1.036958391075404, + 1.004141627720936 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A2/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.181594875000883, + "artifacts": { + "model.bin": "e8d083497ca2af67c8ee759876e97d8d43f69c50ec0ae2da6c331b13f0443ffa", + "training.json": "6f4c30e7256e498d5b44cdd70c9e71890987847ca0b0ca7f3b7b4e6ed9556cf3", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "e0507279bf27dd06b27531e0f09b1e45ad8fcb6f5ab246a136fb45cf3365480e" + }, + "application": "A2", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "logloss": [ + 0.5221071289135859, + 0.49859251594206677, + 0.4795220796388908, + 0.46362837412592345 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A2/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1256351250049192, + "artifacts": { + "model.bin": "353513c013c3e07bfeee7a2eb79e4d63d911723736a81e3ad4c29ea1f3e31443", + "training.json": "2070eb2c933f30530bd05c4517faf93bc495329d6c2995796e4cd5ca86d38a02", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "2c3954053a20c3ae74d37b89a0cfc76d2179e5a2398fb6c9ad73b33566f92f53" + }, + "application": "A2", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "logloss": [ + 0.5209074714903859, + 0.4970019895891687, + 0.4770348203721252, + 0.46038955419628774 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A2/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1675308749981923, + "artifacts": { + "model.bin": "8fc8d7caf918ae34db0e7a36150079d84952fcbe0280b9fcfc6003c1d02d57a7", + "training.json": "ea97f53fba76d1f7789a42d2c5577c04de1e13e93e0145783ef1b70ac38f8777", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "3846c60cd86713862a70a681ac00fc455e5d1369e819d88dd5f829bb904629a7" + }, + "application": "A2", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "logloss": [ + 0.5217617329009279, + 0.498522290455184, + 0.47935847874228055, + 0.46345883603288557 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A2/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.2270602920034435, + "artifacts": { + "model.bin": "e78744df8d21a419237f19727779c49998f089dbe1610119c835770f9fc7e86e", + "training.json": "b538b859632d0cffce1394ecd53562adac9fee92f9c70a4627b571eae9210b0d", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "02f8817c5255ea72f9649c9cb5a008285ab61e5931e5b32d0f90c6db47f373f4" + }, + "application": "A2", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "logloss": [ + 0.5216469355081974, + 0.49872054897034823, + 0.47956748367838464, + 0.46370066280703964 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A2/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1204172500001732, + "artifacts": { + "model.bin": "3d5c8bde562421853b3cf3ab80a2e7a6044f61da216a8a06f5ed4c20b1bdc80a", + "training.json": "a3b825105ee3771f5c82a4fb81478bdd27e2b7019c0c0ca093dc02179462c3c7", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "35d05321eae9eb79e7d0b9f0ff129d52174c5707c7c76199f127faf69aa93a84" + }, + "application": "A2", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "logloss": [ + 0.5224058231450108, + 0.49907051572487793, + 0.4801754436765822, + 0.46413744713338345 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A3/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 3.1047087500046473, + "artifacts": { + "model.bin": "ab5c7bd4b5e8f2af6a87e97fbdccffb06c0cd12c0ca0b3daef90099e3767f702", + "training.json": "a1fc905cfa3096fe2acd35802ccd6059d34888a577bf8bc03ac5300d838518d6", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "e8e6927fb7998658c7f1b1c06ff98835f205ca493906c06c47cfdeac6b7d87b4" + }, + "application": "A3", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "mlogloss": [ + 1.124222720187514, + 1.070145548950732, + 1.029331798483212, + 0.9955857982733826 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A3/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.403268958994886, + "artifacts": { + "model.bin": "326e91ae551175c150b73bd62e1f1bea38c5892d6bcd6817332bca8b89efed0d", + "training.json": "9cc9a218ea61fffd4059b8d1c887b28f1ecd914674d80e70f82139e0509283bf", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "57b26e73e32f82754800390ebea25fae840d2101daec3a32f2132633279023a1" + }, + "application": "A3", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "mlogloss": [ + 1.1234193812481326, + 1.069727341932113, + 1.0289680811642217, + 0.9949726677886164 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A3/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.361048499995377, + "artifacts": { + "model.bin": "4b2548b3f0399b9e3ce250a8a3fc834a0c8306cd923b84f0b86c40d030191a46", + "training.json": "812a0caf31b9eaef5882b06fdf85d2946b0b498edaa4eb810c04b0f3bc407fd5", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "ed793c120d1ac576d93f885685b9945cf60ac06bb5426b58b77097ff0c5aea92" + }, + "application": "A3", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "mlogloss": [ + 1.1237275599966574, + 1.0703036842154392, + 1.0295653049742626, + 0.9956566651730412 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A3/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.380878500000108, + "artifacts": { + "model.bin": "3cb2a8301a09e266d395fde1757cf05cff87dce9f6d5d11560245937eef058cd", + "training.json": "209cc6b9f3932f9ee43e356e9fdf97ecf4e445d35d38f2af7bfc781e42d3352d", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "e2a9646b1a03eee79c0530862757278a404869f80c840b935ddf32d62a9007ae" + }, + "application": "A3", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "mlogloss": [ + 1.123715749175899, + 1.0707980802807204, + 1.0296679925483145, + 0.9960818527324505 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A3/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.4569587919977494, + "artifacts": { + "model.bin": "137b6f1478cd4331fe9ed19850bcc3bbe6fbc81be3576c87a4cd05fa57104882", + "training.json": "8eb03f3bf82777997f03fc772c11f62e920bd97cc70901cec8c51f21674c004b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "7442e9aa7455e4ca59e9b1986508de3c135c70901d6eeb869092adc55d9262d9" + }, + "application": "A3", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "mlogloss": [ + 1.123177087842896, + 1.0697146281226835, + 1.0289672416167297, + 0.995178032257311 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A5/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.221702707989607, + "artifacts": { + "model.bin": "90cf7b077420ccdfdd9d5d171d0e55352eb8b9b8dddebc6807db445102f55ea5", + "training.json": "0872b81d9713f28a86f4ca450719576d9fefc7c6263047fa8878acf4c6c0035b", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "acf11fe38269a49c26bfc642163e2cf1e5d6a0d74823523629b54ffe3c7fd054" + }, + "application": "A5", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "quantile": [ + 31.974066788165487, + 31.695319542592955, + 31.440629697612533, + 31.22046048942174 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A5/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1606928749970393, + "artifacts": { + "model.bin": "3f26df8d5379b3a98704625ead30748f641787571f537c865060925e01e5bd87", + "training.json": "5758c170687640dfaee7d7093d9d9c72157b949dc0029c60b2632a9a0b5e5558", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "b98ea09c6dbba99bd8b779d4bdd0d8fd9c82b434ec3c7d51b01bf02a855c3314" + }, + "application": "A5", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "quantile": [ + 47.94118795875437, + 47.46856293167724, + 46.99976749679156, + 46.5951649898938 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A5/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0546206669969251, + "artifacts": { + "model.bin": "bf2aad6d8d946d2ae3e560fda5c165a360f1afd60c5d9a4db82cf7f051f81085", + "training.json": "cecac736c064d03678f7cf777c14ed6a280e2239066324355c2d30364899b86e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "23d3e75a100e495281ec34161d53e3d5460c5e5b99c6c8f68a5d03fc3f065149" + }, + "application": "A5", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "quantile": [ + 59.85553780503784, + 59.29329994915284, + 58.777271456594576, + 58.249420014191244 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A5/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1602462909941096, + "artifacts": { + "model.bin": "4e07c5f486065e266f7984b1c69be6945670cee0d2005cdb0d23b0731b13a1d1", + "training.json": "8b38e3be724b0f66ae341d22cb971efa3937240f7c6019841db8ca44b3669804", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0c47c820cb593fee0e5ed15b5c5e659630189a1e143354bd84308fb1d0ce7c6c" + }, + "application": "A5", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "quantile": [ + 61.88663157586241, + 61.21408743291521, + 60.582124632486305, + 60.0059411948079 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A5/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1132504169945605, + "artifacts": { + "model.bin": "0b7a6dedc9d099a33bd9f2779322a039b92dc1de6c43788a48fca81e83bfde5f", + "training.json": "403eb4e340fed5a46fd3832acfcdb1edd9f471c757c2526a70c6fd0f8d4691f7", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "22ee32d07565de75da905e69a81cbe01af372a3a7550346f5a5c0f939fc551a5" + }, + "application": "A5", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "quantile": [ + 63.63224392312634, + 62.937176538455546, + 62.287664116873266, + 61.699043841916314 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A6/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.109724375011865, + "artifacts": { + "model.bin": "da89a5766b327896a01105dc042db502b750c50988b83966cff5e691de4d4e07", + "training.json": "6788d336be85fb02678adbb24b58498951d6d9942750dfaf825adf248de97944", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "27c9235cf62fc2ba3ddf30241c590e3f5520b767e905c009a382aa7b19a4d856" + }, + "application": "A6", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 3, + "stopping": [ + { + "selected_rounds": 3, + "history": { + "validation": { + "rmse": [ + 1.0152097818097066, + 1.0090900204206783, + 1.006988270744545, + 1.0101328869699957 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A6/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1138160829868866, + "artifacts": { + "model.bin": "5bc1b61b2e0e257ae48987528d53028020abe3e93b657a95b46bcfbca3608841", + "training.json": "1aa3b5436227d077e75aa4447aebbecf32099780dd1d20b812b209519578b5b9", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "753c256e35fa2117e0e66b0a59e2c642fe06269a887d1320acb40101464a6e61" + }, + "application": "A6", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 0.9064281668733252, + 0.8914249917534706, + 0.8808437264937701, + 0.8737753750518703 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A6/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1678780000074767, + "artifacts": { + "model.bin": "1c46fe98dd8410b051b6c700d94c469f59122f563dc8bd96e5cd6d1da8b63ce4", + "training.json": "2fb6802d3a392be052c9df7cb4366dabea48e99e2810593ce8bc5fe5964f9509", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "24dfcd1ded2e27dbb0fb791bce1b41bc39aafced7fe16c10fcc974a2a3597b6b" + }, + "application": "A6", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.2712642722409764, + 1.2355390257974619, + 1.2084964496812456, + 1.1808598831164345 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A6/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1556122500041965, + "artifacts": { + "model.bin": "16a2344f460d3f8209da7190e25b88ca3244b9501c003ac320d1f3495eace0cf", + "training.json": "cdedbd0c3aee5d907dc8183f8d4d2c024c2798b2dac700dd965d595bd83ee256", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "39037007c526321da71f584e6a6256ae6d5d8230eed16b08849d44d6855bccc7" + }, + "application": "A6", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 2, + "stopping": [ + { + "selected_rounds": 2, + "history": { + "validation": { + "rmse": [ + 0.8482051365361111, + 0.8478712494939465, + 0.8498507877363486, + 0.8534675082907229 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A6/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1179427499882877, + "artifacts": { + "model.bin": "babda4c8797469e36486b8c7de90fcc523b4b5996a1de8c40898dac42a658568", + "training.json": "d43f28849e87ddc9f5a5cae0f15f66456251c35928bc9da1375be2716c4249c6", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1e818da9f56f12e88b87b2873cf6eb4e66dfc1c651d9c761357288a6d896761a" + }, + "application": "A6", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.3698645536405305, + 1.3439158521723147, + 1.3229687919719846, + 1.300697571329999 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A11/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.5771984579914715, + "artifacts": { + "model.bin": "01e4ce1ff1e8f4ffae1b7b9a73c467092f645697596a64afe91cb4745f44ad7d", + "training.json": "5828526feff7915a44761f6a214c2e44e85c2e1341e13a0b196e020507ba95a3", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "38c336c83d00b67d3d32fa234c99f1b2b3b6d7f17d60848a1a4bf37ab8265290" + }, + "application": "A11", + "fold": 0, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5222230477926284, + 1.480984884040511, + 1.4448661314897857, + 1.4192193388805154 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5153939906237581, + 1.4730535147673454, + 1.4368484205236978, + 1.4103263868666862 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A11/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.5213453750038752, + "artifacts": { + "model.bin": "9d01a3710a6386975c0cbe1d1b46b4a155af6b1dc162a47ce70e5856508cb4c5", + "training.json": "adeb392e9d71a5884063ddc9b2c9054bbf859900c3a423bb86dad2d7ede0e87c", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "7b6dfac20f0e618c22a0b10b82076c8d7b24bd85145802cbafae51aa22f9add0" + }, + "application": "A11", + "fold": 1, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5049282105546857, + 1.463238032836992, + 1.4314289942641896, + 1.398575327192213 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5327034390677288, + 1.4874700681826045, + 1.453744239948275, + 1.4199022988363361 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A11/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.51448529199115, + "artifacts": { + "model.bin": "f929a61d700a709434b855069db9ab478dca4093eb058b007fbbaed8c88d8d9b", + "training.json": "38c1ea43b51b5d620038e7afc2243412e802cd706f6075330764c9fe82d76621", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0e3cd067a7312b318f96f2f487ee799747f09f0f763b1a2ab9c7e37d7e7f093c" + }, + "application": "A11", + "fold": 2, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5217132116875307, + 1.4897635505530253, + 1.4517624866431436, + 1.4195040146834654 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5388203405622187, + 1.5057508976405607, + 1.467325684201917, + 1.4351657947544998 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A11/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.5179470840084832, + "artifacts": { + "model.bin": "a21c029d1053702998ab19b43b1cb5ed4925d33d6a056d435a785f24587e3984", + "training.json": "1ab99c8f2084b3dcdc3533da59f7a4c829804afa363754636f9296b985c9f65a", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "eeb9ea8f14964e33fb6b2ae1e075f97a0297a518bf04a1f488e64b69a6a8aeb5" + }, + "application": "A11", + "fold": 3, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5192109500257958, + 1.477542273093424, + 1.442107020019317, + 1.4086702171512198 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5074776988095129, + 1.4667578450478718, + 1.4309549264552512, + 1.3976966352452378 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A11/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.5170913750043837, + "artifacts": { + "model.bin": "57d49f66c1cb996ccd77b0018235c51d9daa20ab97275f87c368524cc8655bec", + "training.json": "73853c9d18517f3a9ba21f8bd3cc00f753f64bff8229ac93f94314d6f02a5444", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "d54e202f5cdcf077cb4f0e8e7b271d98c3cb664a2d32df0d1f6d3e988f934512" + }, + "application": "A11", + "fold": 4, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5230454263784095, + 1.4898330592814988, + 1.4545513374681012, + 1.4206380657402569 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5265866383619924, + 1.4950096984945573, + 1.4605720090766612, + 1.427506882477277 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A12/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1111349999991944, + "artifacts": { + "model.bin": "c30ab91ca8d27684f9badecaecdba0427926fa281320f0b9b0259dcb55b77a50", + "training.json": "8acf5a5cd21a0d056c868e26de99f3e02c0adf5abd526826c429a1c4eac82260", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6ca4bf3a15a9d2f7e67e48363d7f4d6e61408e3ef76e248aaaccec72b9e5d2f4" + }, + "application": "A12", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 14.893546275317354, + 14.310820386115674, + 13.683061806840724, + 13.249780511152489 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A12/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.162068375007948, + "artifacts": { + "model.bin": "0ffe3fff84dc29edb30d256a4fd4390b5c5bc9097f1cf20d100ed4c7e2315227", + "training.json": "00c9d4688d917c7a1b95b4324b5c053735108bc593a009c2bb901275bd14053e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "f710c2b4fd4b6f1a9478ba5384b9946b1efd74ba239699a20ffd4389347de500" + }, + "application": "A12", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.711770268699254, + 15.051416652146338, + 14.554739095401072, + 14.05666347254798 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A12/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1183424579940038, + "artifacts": { + "model.bin": "46c7f63b5b68ce44393a6cb52486bbd950e5efff17759d5ce9cd0243c976856d", + "training.json": "01dce934ac3b346d7dcf15c1ebf8fca900c397196e248871d5f57fb73ce99bf2", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1e504a639102f5a58cccc22d8b524a25f9cde5dd52fc2f32cc05843912db5de0" + }, + "application": "A12", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.19329904042284, + 14.609148043942902, + 14.059621503399761, + 13.46748393578638 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A12/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1720688749919645, + "artifacts": { + "model.bin": "32db553a88e9766df223c7a4d8420d156af1ce0d88315361d401107c267f1a31", + "training.json": "b956a07dc106e29abb2c3930616de06e85781344cfce0f719965adc710743242", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "57951079cbdda8d5fc37d7219dff502a9b6d35ae759ae4be3e76f6fcced645d2" + }, + "application": "A12", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.652135794564133, + 15.083851955094127, + 14.477455842030803, + 13.985865542481408 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-classification-quantile-001/A12/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1067812499968568, + "artifacts": { + "model.bin": "b817e15aa74169a94dbd17cf5078f50875bc4f2fd1432030f2ee655dcd1c7467", + "training.json": "bdbdc8b3661dac2a67da7e74a7e2adf5b94e0c6b80b920a3c4167f69cf685ff8", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6b05f7435a5ecf641ceb64c90d999e3ec9e37ef1c9ee2d678c9a47706de3c5ac" + }, + "application": "A12", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.101568342453419, + 14.48434154845502, + 13.894099076194196, + 13.341288656055848 + ] + } + } + } + ], + "target_scale": null + } + } + ] +} diff --git a/benchmarks/v1/evidence/real-positive-survival-binding-cpu.json b/benchmarks/v1/evidence/real-positive-survival-binding-cpu.json new file mode 100644 index 0000000..0496087 --- /dev/null +++ b/benchmarks/v1/evidence/real-positive-survival-binding-cpu.json @@ -0,0 +1,2329 @@ +{ + "scope": "Real-data validation worker plumbing only; four rounds, no test scores or search selection, no quality or performance claim", + "source_sha": "7e64176a866c9609d0ef917fdbb14655f38a7528", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "-m", + "benchmarks.v1.worker_data_smoke", + "build/v1-worker-positive-survival-001", + "--applications", + "A7", + "A8", + "A9", + "A10" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "device": "cpu", + "gpu": null, + "memory_cap": null, + "threads": 2, + "source_hashes": { + "worker_data.py": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "worker_data_smoke.py": "eecc1aec3a1ffbf6550219f2d9af60dafc213530d742905c18db7223c76e41cc", + "baseline_worker.py": "23a77399fe0d8f22ca8562801e43ea50e6ca492501c2feae278937e8c44b69b0", + "process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A7": { + "application": "A7", + "dataset": "insurance", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "0ae4d0a206449d6df81dbc470fd1654d8f33e72f0f5fbd52bec89c27257671ac" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "ae02673c24506bbcab8b6aa80affd2631d19bd11e5dc14065b23f987beecac59" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "72af1635e878c0524add52ee4c8d8f70cfb0c4705c10f75eb8d0d66a4fd35c56" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "ecc90640de9fce6c1ef627406f672173ea6a2864e198124a0353ea0d1d9175bc" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "12f542a463d871e64bf08e9ea5984779c4ce0167ea59e864d3262900b5722a0b" + } + }, + "metadata": { + "target_units": "period count; exposure is an offset, unit training weights" + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "e0b36f2fbff96b6f8ddacd97603548f8db752cbcce9af696ac1bc3de86f86ba3" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "b18f410fb6e3a5f10fdb2969abff571aa7ffd953dd95931da33a582ac0c63a2e" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "5f8b9d853ea30d7f5f539ba7824e5c5c56ef0a3c10e68fda464be00e645c6aa9" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "2f34597a81ee7b1bda9c5ca59dc0f42b8b9ff9f83e95397fe46774e978a34c05" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "d2e51308260a3463c71ea77ab2d1c2263e3f5fc6d7f0a7c4c484b08f374502f6" + } + }, + "metadata": { + "target_units": "period count; exposure is an offset, unit training weights" + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "c0aa76b4f8a137dfaa3d7d17aa7004445e12681c2e3941e2a25def2e03f7a8c7" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "ab4d1c7b433009c417e74d0f3d65427c5bcb67eabb036e7403a04813f294eb6c" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "d6ee43a322790bbc503a5e241a21ba1f616901efb96d0a2b513381982d86ebb3" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "b55efde0c0ab9b912a7a6a456fb92cd4353f65ccddc92b0c4bdba8a4d0ad8284" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "a937ab72404e6bf594fe0521e71f8027b42668e2305cffd6e684cb51c9922edb" + } + }, + "metadata": { + "target_units": "period count; exposure is an offset, unit training weights" + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "2855dd7fb3504747e141f31126324463b928191c5676e7ed7527c71dd820d407" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "6607785a3a6d5c4cae9c35176d79fc1a1fe1cec82b6023578b3585d08203b528" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "ca4d768608b607071dd949acd6e1f79d65bb89387dd9bb925a5356ca6099a580" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "4011f9ac79a4428c817ab04571cd89adde54dbee0a20478c62f80d8a8e76be54" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "7d02ebaaab85c9125403d2cf88c1c4f91e06e4ab7b847d87969533668ec0ec26" + } + }, + "metadata": { + "target_units": "period count; exposure is an offset, unit training weights" + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "dd2ce518a7a30d3450ac9482d1ac590704b54049db3be7ae5103a4d4cb140545" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "ca2a814621a984aa23b62ef2d1d5e6b94bfc1e470f5cbc5e6abaed46bc22d0c3" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "e8e5c584010cae9fc538baf3e58dcf0c6afe276bb5e350ee269132f771b76145" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "a05ac0c8203720466d313518d36e72d940f2d75b716c28a453ee2a2bcb27fcb4" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "d0820179812781e72c26a433a71de8113c1c83c00b9740dae328bfe030cb082a" + } + }, + "metadata": { + "target_units": "period count; exposure is an offset, unit training weights" + } + } + ], + "source_freeze_sha256": "ff95579dd1b95595e87027f38afd512cd986b3182f08f50a7a2139ffb63f291f", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A8": { + "application": "A8", + "dataset": "insurance", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "03db952aced3525dca5ec3cde4f0eaa6dca3c5212bab622230589564c6dfca8c" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "a0a52a08c3ec5721b50f1b9ae5e01ed24ba9f8a05cfc8a555a9a0a65f902bb90" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "33b30a6a888539777d662a221e772fbb90450b55373537c6050a73d3965617a8" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "54c5440028676ce444f4a4624f50357ac7148dcde7f1be6c8982c261738d7cb7" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "3a255c99d30eb1fbfd9d1c8b9171d86552cdab6857f92cacd532e5c5a620aebe" + } + }, + "metadata": { + "row_identity": "zero-based retained positive joined claim position; group is policy ID" + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "3d6d292ab33e4197544b9c792a426521b41b081a39b5bf1bd46025f57856c0e0" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "b95d35fdb052d7e0cd06a0adca88d710cdce173acf464fcaf9970aa12ab9a131" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "971f4a3b233d466669c895c9fe5ff1a62f9b63b607eed1e915028e2358033c7b" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "716684d70e40449adb55683e76e4489ee978b8d2411ef9cba065a978c3f38ad8" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "c6e5a0382c8d34b4985c4b05099cc31b7ae8500258af9a2ebeb084ca668cbb55" + } + }, + "metadata": { + "row_identity": "zero-based retained positive joined claim position; group is policy ID" + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "01fcbd94e702e793ca2919bdf9d8e3cd8cf2bf3074d1b310424fa0d59fe77f15" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "63a9af461ceb0e2443458610714545957b9797ff00579f41235d0826b6150c3e" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "3175895473a6e26f076fc80732baef91e55846d3f74482c481fff32999a1d73f" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "63cf82193cf0093052db810795d95016c0814557766aa210d001fb8faa67c8ae" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "c3bc7d268c3abeb0ed959e7cd957e4e239c5ebd15de38c932d014918fc6cacc6" + } + }, + "metadata": { + "row_identity": "zero-based retained positive joined claim position; group is policy ID" + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "61415468301edfb6fd706ccf62039d2b80000efccd0e717af24e01c1bebea9d6" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "f0645b5b368af10c5c78992e3273af397db2058ab4a4c61c74a5f0bb9e141f37" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "47e674c02e2baa771dc1cbad9f2e98939c8cbc58b9ca77952c3b66f970753624" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "9d89345272c27e30cc53b0fe62de5d8762ba2d398dde088efdf79219cec1a579" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "0548af93866271046298dbab7b1e21eb8f190468a071d2f74e2ef88249c8b422" + } + }, + "metadata": { + "row_identity": "zero-based retained positive joined claim position; group is policy ID" + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "9fba67e2769974330b33011748b57079326eef3d3e3831cd97d0f7a7ec3eab58" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "747aec63e7f4ce87bc17a5e2356b64948c5237efd7bf9e3d4e67a2a76e98612a" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "6386c1498846e98e30532877c10fc8f74961602df5d5b53a29f1093d329b84d3" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "f8168c59cb3df1c96b52cda6429622667cd9571be9581338cc4cce26f886d5fe" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "a0de74ec3da252ecdf928f4d1d151240052569e7a6191644f49bb2a550b27bb5" + } + }, + "metadata": { + "row_identity": "zero-based retained positive joined claim position; group is policy ID" + } + } + ], + "source_freeze_sha256": "ff95579dd1b95595e87027f38afd512cd986b3182f08f50a7a2139ffb63f291f", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A9": { + "application": "A9", + "dataset": "insurance", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "1e3cd8d7e7bd157745bff7db14534bd1fbcecff3abef8f4341fa1f58a9583f0f" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "89ca61cb3b97a660065989fc29d4083af85a8b78081ff71dba6f316710421bbb" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e9d6286fffd61b5c7dd91b7a027684fa561d72fe0d57a14f0b846b3eae1e8c20" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "6dcf4429e95ed60936e9fce3a8ce1ad4cfe23524dc1ec3115f5ab2ea2a686153" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "0b43c2d68dcea2f014cc1ef202e89fce4055b61723a241d71dbefd6be62efcaf" + }, + "validation-period": { + "path": "0/validation-period.npz", + "sha256": "38b3c8adea524a454dbceb35cb3b18f97a77e332da742607b505cb263d38bdc0" + }, + "test-period": { + "path": "0/test-period.npz", + "sha256": "4a65bf4de6a61bd5434bc9301092c35e4cd0cc636675978d48e0b703978c9a36" + } + }, + "metadata": { + "target_units": "annualized paid total; exposure weight once, no offset" + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "e8c4d49a1008615af63446d04a049fadb9eb2c65243942defc4765272d1e8dac" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "34d0dd6b71e9c4c95461c3eb03fd32da250352b89fc9c113f9c44ff6b9f2f8b9" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "0e80cdbb30a5f39d7a0aa66133f65d5f59bdc9a0cdab73560c3606ff09130f4e" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "8db330118e63840ca6ba192426d475f17c43478418a684fa1283698c80f789b4" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "722b45843d3dfa5494bdf2ad027a398b74d0dbe3ca3ffe3f432d12549bed3a40" + }, + "validation-period": { + "path": "1/validation-period.npz", + "sha256": "cc873cd194e217b151323af8c6ca5078357f5b8fc45c97d0c286d1cd624417a4" + }, + "test-period": { + "path": "1/test-period.npz", + "sha256": "627c93a4f32751fc739ac8a6f1d1b9f6ee36d3213ade9cf5c31847073abdcf7b" + } + }, + "metadata": { + "target_units": "annualized paid total; exposure weight once, no offset" + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "c2a2c40926e231d1e596f7e4c662219d923e6f926a96df27b4717abd7445f420" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "605ce72c18c2ab8b1b00045782ae01f5a7f22bdb38d4ac24074715e39628a3ab" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "55528b0dadba7c2a172ca8eae888750afa418c91ca22991d62873957466b84a9" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "cabecd0aced4f18a10a6029f372f6f7657a45d5fff5d5bbfafedceb7b1190439" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "aba1155b233c75fc269f02c7ce530fda1146d192d7352c4aa9bf1d0b13907e09" + }, + "validation-period": { + "path": "2/validation-period.npz", + "sha256": "7b42a1c4f591debe3866e1d42886de0a5d74c7a58dc9586296c20e2c16dd80c3" + }, + "test-period": { + "path": "2/test-period.npz", + "sha256": "eb23a461b880d1053a00c712426a1f7132d4e1e86c8bfffeffc0db4338cb8438" + } + }, + "metadata": { + "target_units": "annualized paid total; exposure weight once, no offset" + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "7b3cd2b815775626af17f54545b2bbbc1c0bffd23882005f76a8e67e066cfbf2" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "ccc9af5cc82c64fe0ed87d85e8ce2192fc4c685a7c49695a053ec23ff1b49ead" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "5a8f2913e280a26ace47d7d703759109bf6bcc944de930a3aa1bc4f3dc0da054" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "a77c65f78cd60d0ef0765f78721ad123d509c0b911071ea830efa838aee90e2e" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "96ef0af1e84246bec68d60b9d75655873faa375a2d52b436e83e44becd6c4955" + }, + "validation-period": { + "path": "3/validation-period.npz", + "sha256": "1f023d3f7bc39802a59b329474b1e2c80dafd07f29fc6fef6b1fd82447548dc5" + }, + "test-period": { + "path": "3/test-period.npz", + "sha256": "e2c56c144be922042ea2bd671dfd0e99a7d8353d1c8380c767d3418bf7725539" + } + }, + "metadata": { + "target_units": "annualized paid total; exposure weight once, no offset" + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "668e0a57c88afc0ab5b6cf73cd578f7b84c6327670cf4a312f450ca65a558781" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "d11bbf0515574a7e4b7d42ef82e433a7e279f587bf353b8e06fc50db5de1861b" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "cfc3c8aa97ef035aceb7ed96d798f7ee922c4a2e86c076e4ddd7431b2ee6a27d" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "df794e119ee5272352c86b5400198061d8a289ccf782ff85c4af81c2d1d22abd" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "ffd62d22198e3cbd25e42308cc6e1509cbc5c386fdecd66bf7282ac8292196c8" + }, + "validation-period": { + "path": "4/validation-period.npz", + "sha256": "e5f545a742edbd9e9a768ad386f2ce59430c3d2a8c7dec99a95edd9885a91b91" + }, + "test-period": { + "path": "4/test-period.npz", + "sha256": "19a4f4480b0c627a83f3a331fe94004368effa70aa56eb0959d7fdec5bccfbbd" + } + }, + "metadata": { + "target_units": "annualized paid total; exposure weight once, no offset" + } + } + ], + "source_freeze_sha256": "ff95579dd1b95595e87027f38afd512cd986b3182f08f50a7a2139ffb63f291f", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A10": { + "application": "A10", + "dataset": "veteran", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "b46c7c5faa65cdb3f616e54dc5e771b3b6d5368b7a126b5cbb2c182056efafcd" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "512bac52ced4ef2af4ede510794725fe168d2eca943379b7df4f19c892a8ef07" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e36d420b130310c437be3f66b0205a8c0b53707f001cd337add8d50e8cae1b19" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "19487e0beeb624dfe4a91da55b42ac2d399eeef8ab9415e2c217aab127d8729e" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "0cd10008dc9bf1489c0d0dca4168255d951b273e596ce6d8a892b0a92683b27b" + }, + "censoring": { + "path": "0/censoring.json", + "sha256": "b14818e27c68515cf319b317529c7d8350d5b408813c60146310f5053940cf84" + } + }, + "metadata": { + "censoring_support": { + "times": [ + 4.0, + 7.0, + 8.0, + 10.0, + 12.0, + 13.0, + 15.0, + 19.0, + 20.0, + 21.0, + 22.0, + 24.0, + 25.0, + 27.0, + 29.0, + 30.0, + 31.0, + 33.0, + 35.0, + 36.0, + 42.0, + 43.0, + 51.0, + 52.0, + 53.0, + 54.0, + 59.0, + 61.0, + 73.0, + 80.0, + 82.0, + 87.0, + 92.0, + 97.0, + 99.0, + 111.0, + 117.0, + 118.0, + 122.0, + 123.0, + 126.0, + 139.0, + 140.0, + 144.0, + 151.0, + 153.0, + 164.0, + 177.0, + 182.0, + 186.0, + 216.0, + 228.0, + 231.0, + 242.0, + 278.0, + 283.0, + 314.0, + 340.0, + 357.0, + 378.0, + 389.0, + 392.0, + 411.0, + 467.0, + 553.0, + 991.0, + 999.0 + ], + "survival": [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.975, + 0.975, + 0.9493421052631579, + 0.9493421052631579, + 0.9493421052631579, + 0.9493421052631579, + 0.9493421052631579, + 0.9493421052631579, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.9166061705989111, + 0.8707758620689655, + 0.8707758620689655, + 0.8707758620689655, + 0.8707758620689655, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345, + 0.8127241379310345 + ], + "strict_upper": 999.0, + "grid": [ + 13.0, + 25.0, + 35.0, + 53.0, + 87.0, + 117.0, + 144.00000000000006, + 228.0, + 357.0 + ], + "tie_rule": "events removed before censoring risk" + } + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "812c66eaa230f736907f4f84cfee8a393bc9c347352f919ef853d1ccfaf03813" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "a4d168b7aae5b6f7ebe9fbb631ea35bf5ffdc9bff63cd5c76d713e2b96d39fc8" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "bff1110a8c722c4a5012e39611a0268f88d58f35fc7d79d7a3aea51105a48b31" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "34baa5e4427ca7b96c270d60a7343ed6e49c2a3e792d2611fcbf1de58c996e65" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "1733238b3ae3e757fb30363071f9ca1d2d6d3b86a28c54790e1a7f799a336926" + }, + "censoring": { + "path": "1/censoring.json", + "sha256": "e3c4e01883d9131b32cf907ced855710919b78a36b143c587bc57a5ed5db4d97" + } + }, + "metadata": { + "censoring_support": { + "times": [ + 2.0, + 3.0, + 4.0, + 7.0, + 8.0, + 10.0, + 12.0, + 13.0, + 15.0, + 16.0, + 18.0, + 19.0, + 20.0, + 21.0, + 22.0, + 24.0, + 25.0, + 27.0, + 30.0, + 31.0, + 35.0, + 43.0, + 44.0, + 45.0, + 48.0, + 51.0, + 52.0, + 53.0, + 54.0, + 56.0, + 59.0, + 63.0, + 73.0, + 82.0, + 90.0, + 99.0, + 100.0, + 103.0, + 105.0, + 110.0, + 112.0, + 117.0, + 122.0, + 123.0, + 126.0, + 132.0, + 139.0, + 140.0, + 143.0, + 151.0, + 162.0, + 164.0, + 182.0, + 216.0, + 242.0, + 260.0, + 278.0, + 283.0, + 287.0, + 314.0, + 340.0, + 384.0, + 392.0, + 467.0, + 587.0, + 991.0, + 999.0 + ], + "survival": [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9532614278376991, + 0.9225110591977733, + 0.9225110591977733, + 0.9225110591977733, + 0.9225110591977733, + 0.9225110591977733, + 0.9225110591977733, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8840730983978662, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085, + 0.8251348918380085 + ], + "strict_upper": 999.0, + "grid": [ + 13.0, + 21.0, + 30.0, + 48.0, + 59.0, + 100.0, + 122.0, + 162.0, + 287.0 + ], + "tie_rule": "events removed before censoring risk" + } + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "d3cc59fa8a1ba44777bcb366ca375becb9ae54778915882f2b6ab0b0341b8d32" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "f18d5c0cf3d8a80b76681bc37147abe8ddc17a2f62a39778525c21320cb6066c" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "6f827ea44dcc7269cefab9b2f922efd88827eb5fb7cd4c7061d841a15c31a0c6" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "90bbe12444fd37d3ec88118e2d630cbce9d1cee8c01ef9c4cf30a68e7c526998" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "923910be22567d05310b0cd036127877ee76dcc2f43cdae2c0c13aef6c8e21f9" + }, + "censoring": { + "path": "2/censoring.json", + "sha256": "e3c1722ae5274ffd338b0281812abde037fda1e482067988d775670e1997e215" + } + }, + "metadata": { + "censoring_support": { + "times": [ + 1.0, + 2.0, + 3.0, + 4.0, + 8.0, + 10.0, + 13.0, + 15.0, + 16.0, + 18.0, + 19.0, + 20.0, + 21.0, + 22.0, + 24.0, + 25.0, + 27.0, + 29.0, + 30.0, + 31.0, + 36.0, + 42.0, + 45.0, + 48.0, + 49.0, + 51.0, + 52.0, + 53.0, + 59.0, + 73.0, + 80.0, + 82.0, + 83.0, + 87.0, + 90.0, + 92.0, + 95.0, + 99.0, + 103.0, + 110.0, + 111.0, + 112.0, + 118.0, + 122.0, + 123.0, + 126.0, + 132.0, + 133.0, + 139.0, + 140.0, + 144.0, + 153.0, + 162.0, + 164.0, + 201.0, + 216.0, + 231.0, + 242.0, + 260.0, + 278.0, + 314.0, + 384.0, + 389.0, + 392.0, + 553.0, + 587.0 + ], + "survival": [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.9743589743589743, + 0.9480249480249481, + 0.9480249480249481, + 0.9480249480249481, + 0.9480249480249481, + 0.9480249480249481, + 0.9164241164241165, + 0.9164241164241165, + 0.9164241164241165, + 0.9164241164241165, + 0.9164241164241165, + 0.9164241164241165, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.8782397782397783, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005, + 0.7904158004158005 + ], + "strict_upper": 587.0, + "grid": [ + 13.0, + 20.0, + 29.000000000000004, + 49.0, + 80.0, + 99.0, + 122.0, + 153.0, + 242.0 + ], + "tie_rule": "events removed before censoring risk" + } + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "8bd3e638350bf9122f22af900a959dc28a132f8ca79f3522bf2eb228b694eef3" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "2853ecdf3e18994ccfd9d53dcd7734c239fc2532a0e7dfdcce0b298bc3debf78" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "3423bcc22a47c0723b0ca0d0ab8767bfa6ad1109d34c93ccee5bf48e1abcefc4" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "bb918d3abebb9f7f7db97fe35d8400f3dbcbc6e9039efe7934ffe765da9b6950" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "fc3d4dfb3f40aeefc1b98ddc6c45276279473ab48b3276c40e0c61e72534b922" + }, + "censoring": { + "path": "3/censoring.json", + "sha256": "7234f1ee28dab66d1b8ab9ee323273009e4069f05b5aa62ba774104dc21a79b6" + } + }, + "metadata": { + "censoring_support": { + "times": [ + 1.0, + 2.0, + 3.0, + 4.0, + 7.0, + 8.0, + 10.0, + 11.0, + 12.0, + 13.0, + 15.0, + 19.0, + 20.0, + 21.0, + 22.0, + 24.0, + 25.0, + 27.0, + 30.0, + 31.0, + 33.0, + 35.0, + 36.0, + 45.0, + 49.0, + 51.0, + 52.0, + 53.0, + 54.0, + 56.0, + 72.0, + 80.0, + 84.0, + 87.0, + 90.0, + 92.0, + 95.0, + 99.0, + 100.0, + 103.0, + 105.0, + 111.0, + 117.0, + 118.0, + 122.0, + 123.0, + 126.0, + 132.0, + 139.0, + 140.0, + 144.0, + 151.0, + 153.0, + 156.0, + 162.0, + 164.0, + 182.0, + 228.0, + 231.0, + 242.0, + 250.0, + 278.0, + 287.0, + 340.0, + 389.0, + 411.0, + 467.0, + 553.0, + 991.0, + 999.0 + ], + "survival": [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9830508474576272, + 0.9532614278376991, + 0.923472008217771, + 0.923472008217771, + 0.923472008217771, + 0.923472008217771, + 0.923472008217771, + 0.923472008217771, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8865331278890601, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416, + 0.8232093330398416 + ], + "strict_upper": 999.0, + "grid": [ + 11.0, + 21.0, + 30.000000000000004, + 52.0, + 87.0, + 100.0, + 123.00000000000003, + 156.0, + 278.0 + ], + "tie_rule": "events removed before censoring risk" + } + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "edef5619d2912c008b5c3086586c999138ccb1ae5e7e226e07bbfd5820d19f0a" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "cee772b41d946c6fd3fd2eb47be0478868242d9e099a89bf74894e3d3279832a" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "3c79f4937978fcd01526cbd2ada24a14d74c85592229bdadd8ee75be39d32c51" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "932132f5b11b85385e1d6fa1ee14e91c8a82bf46e88b2991290fee4846f8effd" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "0a98c618ef1ca9945912c236a0fa758e8537557d0d6ba9db33f91ce97e79e7a2" + }, + "censoring": { + "path": "4/censoring.json", + "sha256": "d70df2478c19e88e51134bef9d1dca62966f8c25f3ecc888aa704457ce8d1efa" + } + }, + "metadata": { + "censoring_support": { + "times": [ + 7.0, + 8.0, + 10.0, + 11.0, + 12.0, + 15.0, + 18.0, + 22.0, + 24.0, + 25.0, + 27.0, + 29.0, + 30.0, + 31.0, + 33.0, + 35.0, + 36.0, + 43.0, + 48.0, + 51.0, + 52.0, + 54.0, + 63.0, + 72.0, + 82.0, + 83.0, + 84.0, + 87.0, + 92.0, + 95.0, + 99.0, + 100.0, + 103.0, + 110.0, + 111.0, + 112.0, + 117.0, + 122.0, + 123.0, + 126.0, + 132.0, + 139.0, + 143.0, + 144.0, + 153.0, + 156.0, + 162.0, + 186.0, + 200.0, + 201.0, + 216.0, + 228.0, + 231.0, + 260.0, + 278.0, + 287.0, + 314.0, + 378.0, + 384.0, + 389.0, + 392.0, + 467.0, + 587.0, + 991.0, + 999.0 + ], + "survival": [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9841269841269842, + 0.9617604617604618, + 0.9617604617604618, + 0.9617604617604618, + 0.9617604617604618, + 0.9617604617604618, + 0.9617604617604618, + 0.9350448933782267, + 0.9350448933782267, + 0.9350448933782267, + 0.9350448933782267, + 0.9350448933782267, + 0.9350448933782267, + 0.9350448933782267, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.9028019660203569, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868, + 0.833355660941868 + ], + "strict_upper": 999.0, + "grid": [ + 11.0, + 25.0, + 33.00000000000001, + 52.0, + 92.0, + 111.0, + 143.0, + 201.0, + 314.0 + ], + "tie_rule": "events removed before censoring risk" + } + } + } + ], + "source_freeze_sha256": "daa35b5b691db5e8ae4f2d804ac60070863913fb22f02187adfdac7b0b95c84d", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "6d2b09303abaace59b22aebd666317b2da57a0efd37f8cbfb138766a933749f1", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A7/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.168413125007646, + "artifacts": { + "model.bin": "2c6cf0b910adaca44b782079bac424838adbd829a5f1c1a57428a6d767c6d320", + "training.json": "cd51ebbfff004d960463dd58ad837bdf553d946232abf25346577d4cdbcad865", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "f546ee5675de194af160690007a74a5f5275e3dc922612f6d0688e15565b5400" + }, + "application": "A7", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "poisson-nloglik": [ + 0.212856665331469, + 0.21231485175178436, + 0.2117806474360564, + 0.21127564429907983 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A7/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.7269726249942323, + "artifacts": { + "model.bin": "9707c6b7e7f42ad062fcfcd6d42389f54f3375b07295f70b5a79e6bd8f3e698c", + "training.json": "c1f748546183edfbea2ded76ddeb2d12f1de150b401b1c8e59f3bf3dab53dc7e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "b0dfcdda9da4267f61cf53c28c51094dbc665b94dc0f5da94b9b399a3aad0155" + }, + "application": "A7", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "poisson-nloglik": [ + 0.21982441482327875, + 0.21922480381867532, + 0.218657480432984, + 0.21810482048052024 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A7/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.7059634589968482, + "artifacts": { + "model.bin": "e1da0ac526e6425f5eefc8f0b0c67656ee041543be31867446775899610401cf", + "training.json": "0600c293aa5aa4dd1a153d4e310b74d5f616905dc355ee7101b57255d09ad8ac", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1cef87929fd6ba2591adc4a02300d25062d3b1f4142f293948026094702eb667" + }, + "application": "A7", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "poisson-nloglik": [ + 0.21369409200762787, + 0.2131007307204482, + 0.21250679512063386, + 0.21195018149494782 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A7/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.7050566249963595, + "artifacts": { + "model.bin": "c44014465b1a665621a996be4e5fc076c3de6bba133d808c830b49fffba554f4", + "training.json": "2f3d6a85cd9cef2ea8233bf167b90b78244167eb2a7cefdfc87a15dd190ec163", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "a914bf341c0e302da05d6f463e5e191ff24fa4ca78d3dc5bbcc1514356772763" + }, + "application": "A7", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "poisson-nloglik": [ + 0.21544828531689675, + 0.21485549251646016, + 0.21428456107476293, + 0.2137408869698876 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A7/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.6101616669911891, + "artifacts": { + "model.bin": "4c9241f897b9953fcb41172fe7270bc344a6e8c9e28427a854fd867f7c8c481d", + "training.json": "a03a571a8179363726e0c49f2573fa7706b8df926a778ae5d1ae7e6bf2d0b5f4", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "30d81b352387cf0d673ac9ce153b3895daedc1927f4005539b0fe9c033d98da9" + }, + "application": "A7", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "poisson-nloglik": [ + 0.21439550835066978, + 0.21384721007037913, + 0.21331977720188375, + 0.2127522896128628 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A8/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1320687090046704, + "artifacts": { + "model.bin": "a59c20b0eaf4aa561d65523dfc690b7a79a3c707a11c2db726ae631785b2e892", + "training.json": "87751e5c2814cb1eb4b3e302f480677f2c2f06e0af02b137ec4c6de941906c96", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "81b837a0ff44755810ff86e49a8959355f4fc292d442ba3499b39641e20f5855" + }, + "application": "A8", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 1, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "gamma-deviance": [ + 1.7295517443975177, + 1.7310231820347421, + 1.7330472533779555, + 1.7354325598679154 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A8/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1744736660039052, + "artifacts": { + "model.bin": "be9cebf5065b7924fd173f37cbfd2be666ec7bfe2536960a3f58f7b602987d1f", + "training.json": "83de8d4601b47d0e00c4b107e2e351ca360e79691f81898d4b7d48ab2c4857b7", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "772f8602deacc5f3208055c9105f3b2e12f98606e441ee0abff53532dca5deca" + }, + "application": "A8", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "gamma-deviance": [ + 1.831063456996314, + 1.82554241331858, + 1.8190115881500777, + 1.8171090499549813 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A8/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1827809159876779, + "artifacts": { + "model.bin": "642c6c446bc6e0b897ec3568f56b70b09b9d20cd65b3538d2694dfabbe5d1105", + "training.json": "f784ce4cae16b53a82e4e6b22d90634746bddd8d13685c6e1aebf90b71bf2dd6", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "3b1d2dc76855c95b92ae412d1e2b3cd68ad69569e28dd7bd70b83a468444d15d" + }, + "application": "A8", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "gamma-deviance": [ + 1.6702219194178436, + 1.6640765426508486, + 1.6594285031360096, + 1.6552576835623445 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A8/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.3260123750078492, + "artifacts": { + "model.bin": "a7fbef7bab1254ce9e2221df0fd48987dd030401ca43d6e017957d9ca33185cd", + "training.json": "05556cc46ff4f0cc81d9f38139140046d8a2d8a8fad778a98ba2915d54acbc7e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "f70271a490bc1e083ae3357c332d967846cf76bad03cc437d9874319da0bc4c9" + }, + "application": "A8", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "gamma-deviance": [ + 1.345808005319315, + 1.3432433329151998, + 1.3415275324133888, + 1.340494034834219 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A8/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.4955464170052437, + "artifacts": { + "model.bin": "bab1ac738f8f7d0cdccb184c2164d60b47fb899af6c21a931c94808ed7e5fdb9", + "training.json": "449f9e25ccc686e43827a4e4dec255828bedeb64309cdc1c6c1f93f2b6d22e75", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "3e72632383fdbb33e1fc03b0a6808f81c29433bc6a16b3ca3872102ccc0a1cec" + }, + "application": "A8", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 2, + "stopping": [ + { + "selected_rounds": 2, + "history": { + "validation": { + "gamma-deviance": [ + 1.389290035543888, + 1.3883576631949284, + 1.3888961848986818, + 1.3887524140595808 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A9/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 2.4369379580020905, + "artifacts": { + "model.bin": "d4763725e3dfc7466463774ae9f3c3bb9bed9ec50d77dd7af7116de75c328d06", + "training.json": "e4eaba41108848c75739365c522879049fe4f47263363e227a5af971f3e9ab71", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "de5b9c48176bafb2ca0f6f50b2552b63a19a75f4c0a88d48a786f3f59983ba78" + }, + "application": "A9", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 52.14906288425846, + 51.957170024263824, + 51.79177555890604, + 51.67460321621662 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A9/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.7749429590039654, + "artifacts": { + "model.bin": "0a6f2e758b705be1639bf4dc5d33d35b1b0efd9fb3baa99084b118837eeb300b", + "training.json": "b8fd6bb11d310e7e95fd8204a4209803efa527f276a7aee5ea30e36a532025e3", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "c96bcdfdc957b9c0e579c0794ab3a07d1f2967e2bf0adbccefb62733043762d3" + }, + "application": "A9", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 53.39811488735966, + 53.00187163281508, + 52.65604421198682, + 52.38109618472195 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A9/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.7555047920031939, + "artifacts": { + "model.bin": "0d6d21f041c02be7f7b40d0cdd77bd5794826547db0646b1b89588d0c208ce15", + "training.json": "d812182889f05f92b6ada1ce6828888f113e3eddc485b1df43e46bd97668ba13", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0638d345dca1aa85101f1bdc24217f29764230270136750fd8412ef60ba0bf2e" + }, + "application": "A9", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 50.24732490590859, + 49.79886345768801, + 49.40961316842392, + 49.06945504688432 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A9/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.651383958000224, + "artifacts": { + "model.bin": "19ba950ee45fdf6f97f3bf8a1b58ed349bd9411972b9e1afed524109b49c43e1", + "training.json": "772b993a0593393ed84e633f0c616cbe05ea1705da4d70544d945d8557789c2f", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0065c99a7b8cff4d42ce8060ba2d18e087b4933b5b1490e6cb8e3ce5e8d8e2dc" + }, + "application": "A9", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 46.857653574174044, + 46.67954583816954, + 46.52104201233198, + 46.38517572118167 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A9/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.6602982500044163, + "artifacts": { + "model.bin": "7ac95bb7c9d6136faa5c45e9af15c3ac163e0de32328fc98c7f9d0a2f3d8ee45", + "training.json": "634ed691e13b89bc0b35d9df9de38401567b13488a0443d87344881f54e88a9d", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "b7a8e5cc5de877ba30a4a016b932ffebea60320a47a6bd1767010cec0bd68af2" + }, + "application": "A9", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 47.09109566237687, + 46.861772673488474, + 46.66488720723748, + 46.49625394342061 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A10/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.3898752919922117, + "artifacts": { + "model.bin": "30cbcc481529dda44fa62ff59cd1d2312907ac74a1237ff30b3302146df20e03", + "training.json": "a81f31494fc7e6d0363d81ea1b72ad52d4ae52bbe989a61b464fd8d9ffe29f0e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "4dbf2c6cafb9c46d8daa94d1bd3c523bdad9b5fbebaf43d5b2b3053b658a360e" + }, + "application": "A10", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "aft-nloglik": [ + 14.119231461348566, + 12.390939992348333, + 11.080796545111381, + 9.900725795182966 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A10/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1576068750000559, + "artifacts": { + "model.bin": "cb626236a1a9b29dbb87dc8924c9741b76cbd0ab139c9dca9180fcc3eb34363f", + "training.json": "77faeff2705413000b18ca715679440ced9b79b7b4009991d2a179ecc992ad51", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "85c307f57a88d764e8dd9347b112de485de6eaf6c7616b2457effd4443266735" + }, + "application": "A10", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "aft-nloglik": [ + 14.224506008106774, + 12.438404184931063, + 10.996652044321483, + 9.830153483295659 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A10/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.1702475830097683, + "artifacts": { + "model.bin": "430bf5342c2070969dd798780635b702d3886e72818a0565b5852ae45e066b34", + "training.json": "ad0963e6035ed4e04936a242535b610ba610646ceae22795ee9ecb3a7a361352", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "690e5a8a919dcae51690a04f4a351b8914c5cb410975a2ef00abc5f58536cc06" + }, + "application": "A10", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "aft-nloglik": [ + 14.7369516556354, + 13.010274545222087, + 11.556047423517352, + 10.345953276223725 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A10/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.2233430419873912, + "artifacts": { + "model.bin": "0880b28b8097aa4e2e4a2c6f9212c431ec02483618fd49874494dbe7f2ecc84b", + "training.json": "c98d687b8fa6644cd5bccc2851c1fc701d242b626a7b66212d0a26ecb5616fb1", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "d16a5fbb403291769a93d907c3e55c55d3be38907322f469c40b506c04f027e9" + }, + "application": "A10", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "aft-nloglik": [ + 15.078735539756986, + 13.361840215733737, + 11.95188874774984, + 10.798564298566605 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-positive-survival-001/A10/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.172200166998664, + "artifacts": { + "model.bin": "30b338c148689eebe784a0e05fb25197a577ff2d80d39a17036cc70d95d9a679", + "training.json": "3992c1110bb68e1b45cf3134e83243897dfeba8391370dcfcc2fbf17de05d552", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1ef3ae78fab202efc0a47cbaceda0eef981c4e6a621202e9a01e4c8adc5ad3e9" + }, + "application": "A10", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "aft-nloglik": [ + 12.718391968497404, + 11.13434621034207, + 9.858743670097324, + 8.818336434184383 + ] + } + } + } + ], + "target_scale": null + } + } + ] +} diff --git a/benchmarks/v1/evidence/real-worker-binding-cpu.json b/benchmarks/v1/evidence/real-worker-binding-cpu.json new file mode 100644 index 0000000..010dcd0 --- /dev/null +++ b/benchmarks/v1/evidence/real-worker-binding-cpu.json @@ -0,0 +1,1709 @@ +{ + "scope": "Real-data validation worker plumbing only; four rounds, no test scores or search selection, no quality or performance claim", + "source_sha": "2952c137f395e9de32229953f31f2ba94fada319", + "dirty": true, + "argv": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "-m", + "benchmarks.v1.worker_data_smoke", + "build/v1-worker-data-smoke-003" + ], + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "device": "cpu", + "gpu": null, + "memory_cap": null, + "threads": 2, + "source_hashes": { + "worker_data.py": "f5d7c152bddb250a8e0f42243d033aa5380ef9ff31f15455ef85892bb5a5f684", + "worker_data_smoke.py": "94b776393041192ded6ba1c150dbb18090cff3afbcb6e646815b19a3adcda6ee", + "baseline_worker.py": "23a77399fe0d8f22ca8562801e43ea50e6ca492501c2feae278937e8c44b69b0", + "process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab" + }, + "data": { + "A1": { + "application": "A1", + "dataset": "housing", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "ee9fb6d9cbbc8ee3088413ffefb7467c221df5606372269f81b5225087007928" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "610e546bd63e5b7ad2c1be26db958fd00536b6d9e4026f102e29ebfcf003cece" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e4f404f717fa4b6c6fbea5819f4fa572336d4b96fd351c824a27373d78eba473" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "cacb45b11d4ceeb3997895e9bee1398a2bf8a8711fabffdc1dedd96d19e66f10" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "07180ad525f246a21d67918781b061f53b76249f9775e8aa010ad39eaa1b2631" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "9b73a363988ddc95d2b7030ca1b387d22e9e7f44bc30f694058f251f718f4a7e" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "0c5fe3cf4b800fee9346ffc9214815d6f93064b3a0482fc15b78e231d3888102" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "492984cc05178c60813ddcb62e798c4c9aaa075273537b2d5bc7ed5f2cbf438d" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4130a6ee488ec32a4555f3f9f55bbbc5dac85ee4f55b1d2a6d6d0c0bcb35e82f" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "f0d1d8c9180e58156138c18fbfa7e9b10fbba3aac5fcf26054f0c6e84d0fbd37" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bdc4220a156117b1656b4867ef417c1988eb89fa8cf6d8f19a41b17214845305" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "4d267b0afe6968592ea8010a34aa7571922c900d6a2baa264d3ecac8f44021db" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4ab22696f06c57eb49ec3f94788aa5a59e8417c3eaaf1f994b3210a3b34ed327" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "66749ebd46b211b0d8d8f4b9efb458a5ffcd5a3620ed6b0766f5d0e312b8e20a" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "1442212a3dd1bad69b848fef0a9d4746688e614edfe4f99cb84add5c3630a1b1" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3fc154610f4ff93c172802d57afa274c299940c638dacd4869082c8bc5bc3c51" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "a80fd6416a556871e3f57ea5924f6b5f26adca2a55e9f6991495777600b4a2f5" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "98fdf48b758b632e87581c66517e9c9b89e09e46f4fbeca3eb7ccbba3e443e67" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "d52e364a351d6424c29c95d226d3962c9727e9b61acfbd5ec9861bddd3444ed3" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "d02417bd952ef72b6da1865cfac5f7667ef329897eaac3bc817b3dd291ab4369" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "a3cd76073e4a14f666ed41182287b86009dbde10ec7cc0637926aa30d3b46783" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "b798bb52c90d3c9866582b355d3df6cfd690e97fa32cfe5b69cd19d8f9bb6a41" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "677e837444eb3338e6a23a93f2ab693bb5afd1b96c3ea08023af41c4befb29ce" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bbf8716a861ff6d59ce784d7b0e948c43484f8f2226c9f0f601a1ed01195d613" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "f769ecfe75a7cf24ce3ad9ae86132365333baf65c7372de2c8f03960636a790d" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "93a5e050bdf4c3ae0c526fb0a75bd806df404c8ab558e3019bcc0bdcd28448ae", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "f5d7c152bddb250a8e0f42243d033aa5380ef9ff31f15455ef85892bb5a5f684", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A6": { + "application": "A6", + "dataset": "parkinsons", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "0750282946e2e2e5636117db6564652c504b1958ff363c20474816dd3a849f53" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "cbd0f8b308a4b6bcf1994ccc52fc416753db2c7d0544cdc981755061963cbc48" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "018f27e04c960f316c425def9a9dc425ec9033c8d06f208acba646a0e23145ea" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "45e6fdeb7bdc716d4972005915c979eb699c1c830856a50d2212a05fa3611848" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "92cf249be956646637158b98bfe1808716c93e21333a3baccac4ce065e8d9991" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "5eca1356971580b2aa677ccbc3f8abe69b64c7478cd67f7fd25da1e029f7db25" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "9805269dd0594f6e654f4bc64df8694a02a34ffde8474468e4865df7bce4d1d8" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "4947fd9a0515b4646b9638256cf7bb57adf899762c8212a9c58402b0a3dade7f" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "827a1797ca4d64ec7722f4dbb76d2e874a575366eb12ca4e5c07aef15134066a" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "eb6829963b9cdeb2c70583ef5b38d883a4978c9b185e175f81b3302a405d636f" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "79189bbfde0c2e13fa27728ce8a6ead553065da83ad6d94fadaae6163d453296" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "d58b5d592d0c38ab1c7258046dc84598a57f1d447aca23cc750d99835b76f31a" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "c84b2ba1d2884415b029e050876a16d454f1c50d93f1d5804155ecd426d198e8" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "7a81dc178bd308c3799916ba5265742bcd9ca96383ab13f7d4d4ea415dee1a5b" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "c6a3198bfcde399f9add245ef86bae57d7a69457c46dddb48346c98e79efc979" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "cab04f13105a6e4c630bde542ab425099f4422c7b3dbb7f22e9f75aaac8c5406" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "545a95c32e526be45407754189eb4261559fc1e9fdd3b54bf0c19015da69636d" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "df4b66ebc77833c5df46e730f361f14e7f1728ec05883d22fb3208438f398c46" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "6a9d99374e40349110d4b525dcb6c97bee5cddc849de65b1d00da6b9a35de557" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "f696ba5fed863349a0bc216cfeb903a13e9d54ac44b57135c372dcc8759cb08a" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "b0ae47990cc48c72afc47dfcf2273c3cedc5d606289d0751ee2afa3edc465b67" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "4e0f851b54a87b26273eee75ac8d9c20243ea95324ef0329983ca127e9ae2905" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "b32c3fd08cec7781deb95a7d01057e2d8d2d8b0cd63c8c1368b4f5104a8ed95c" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "1d9081dee48c899212497db8d80630f81dd7d934bea31d00cc5781b92666ba0c" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "150f918c1496d3d6fc8062614ce7b592ee00d68a590464f52c50aefb6833b9cc" + } + }, + "metadata": { + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + } + } + } + ], + "source_freeze_sha256": "334cbda3dbd18849914a0069b6203035280cf5a716838b098a93856c4a2140f3", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "f5d7c152bddb250a8e0f42243d033aa5380ef9ff31f15455ef85892bb5a5f684", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A11": { + "application": "A11", + "dataset": "housing", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "ee9fb6d9cbbc8ee3088413ffefb7467c221df5606372269f81b5225087007928" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "610e546bd63e5b7ad2c1be26db958fd00536b6d9e4026f102e29ebfcf003cece" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "e4f404f717fa4b6c6fbea5819f4fa572336d4b96fd351c824a27373d78eba473" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "cacb45b11d4ceeb3997895e9bee1398a2bf8a8711fabffdc1dedd96d19e66f10" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "07180ad525f246a21d67918781b061f53b76249f9775e8aa010ad39eaa1b2631" + } + }, + "metadata": {} + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "9b73a363988ddc95d2b7030ca1b387d22e9e7f44bc30f694058f251f718f4a7e" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "0c5fe3cf4b800fee9346ffc9214815d6f93064b3a0482fc15b78e231d3888102" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "492984cc05178c60813ddcb62e798c4c9aaa075273537b2d5bc7ed5f2cbf438d" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "4130a6ee488ec32a4555f3f9f55bbbc5dac85ee4f55b1d2a6d6d0c0bcb35e82f" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "f0d1d8c9180e58156138c18fbfa7e9b10fbba3aac5fcf26054f0c6e84d0fbd37" + } + }, + "metadata": {} + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "bdc4220a156117b1656b4867ef417c1988eb89fa8cf6d8f19a41b17214845305" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "4d267b0afe6968592ea8010a34aa7571922c900d6a2baa264d3ecac8f44021db" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "4ab22696f06c57eb49ec3f94788aa5a59e8417c3eaaf1f994b3210a3b34ed327" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "66749ebd46b211b0d8d8f4b9efb458a5ffcd5a3620ed6b0766f5d0e312b8e20a" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "1442212a3dd1bad69b848fef0a9d4746688e614edfe4f99cb84add5c3630a1b1" + } + }, + "metadata": {} + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "3fc154610f4ff93c172802d57afa274c299940c638dacd4869082c8bc5bc3c51" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "a80fd6416a556871e3f57ea5924f6b5f26adca2a55e9f6991495777600b4a2f5" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "98fdf48b758b632e87581c66517e9c9b89e09e46f4fbeca3eb7ccbba3e443e67" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "d52e364a351d6424c29c95d226d3962c9727e9b61acfbd5ec9861bddd3444ed3" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "d02417bd952ef72b6da1865cfac5f7667ef329897eaac3bc817b3dd291ab4369" + } + }, + "metadata": {} + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "a3cd76073e4a14f666ed41182287b86009dbde10ec7cc0637926aa30d3b46783" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "b798bb52c90d3c9866582b355d3df6cfd690e97fa32cfe5b69cd19d8f9bb6a41" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "677e837444eb3338e6a23a93f2ab693bb5afd1b96c3ea08023af41c4befb29ce" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "bbf8716a861ff6d59ce784d7b0e948c43484f8f2226c9f0f601a1ed01195d613" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "f769ecfe75a7cf24ce3ad9ae86132365333baf65c7372de2c8f03960636a790d" + } + }, + "metadata": {} + } + ], + "source_freeze_sha256": "93a5e050bdf4c3ae0c526fb0a75bd806df404c8ab558e3019bcc0bdcd28448ae", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "f5d7c152bddb250a8e0f42243d033aa5380ef9ff31f15455ef85892bb5a5f684", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + }, + "A12": { + "application": "A12", + "dataset": "concrete", + "folds": [ + { + "seed": 0, + "artifacts": { + "worker-input": { + "path": "0/worker-input.npz", + "sha256": "1409cb024293499551527b6bbf635c9d01e97de7731ec170d4e5950b13aea531" + }, + "train-rows": { + "path": "0/train-rows.npz", + "sha256": "810fd9c5067134e4de2fe9ab61092908713a2bcb3fc99f9aa86d6835649bf878" + }, + "validation": { + "path": "0/validation.npz", + "sha256": "616c8feb197f7cce51a5c21fa3badb29532f4453d8fb1a212c5e71caa403a969" + }, + "test-features": { + "path": "0/test-features.npz", + "sha256": "2cf36ce99d30c832c86a07a1cf250d8a992f2c2a6d1cdd3089d220eb42c88534" + }, + "test-truth": { + "path": "0/test-truth.npz", + "sha256": "cc6daae2aa8d170f17aa018439cfbbc5e606a9ece3c455a455bf8c1a985e6a3f" + }, + "validation-structure": { + "path": "0/validation-structure.npz", + "sha256": "59ebd7cd5785f96c4f808dcde1ef3709a81b346b6937073bc09f7cb157962a07" + }, + "test-structure": { + "path": "0/test-structure.npz", + "sha256": "05fe7a98b2dc54e8824293afdf17137715ae6d3616919978de9f729a18ebf0d4" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 1, + "artifacts": { + "worker-input": { + "path": "1/worker-input.npz", + "sha256": "d17dd325727de736821bf3435ad5051ae36c00b7d4f7efcf99dcb16dacfbc7b4" + }, + "train-rows": { + "path": "1/train-rows.npz", + "sha256": "2bfd635778f518a16be87778cc72601cd927d9e34be5e6eda638480ac7947cc9" + }, + "validation": { + "path": "1/validation.npz", + "sha256": "8f79da547abcce2ff7c262cf4f25b54f2ca2fd0af13c3f02b4074af02007ce7c" + }, + "test-features": { + "path": "1/test-features.npz", + "sha256": "a4612a16d835cd2d712f37484d5ff3db4f0e2a0c22bde74b581d6dc8bf54df64" + }, + "test-truth": { + "path": "1/test-truth.npz", + "sha256": "19478323cc0ec2d8bf8378c375c29afae01d1dacae29a40a70666f071e49960a" + }, + "validation-structure": { + "path": "1/validation-structure.npz", + "sha256": "b8e961ee37afa13f939ac261834c328667f1aaf44c8f88db85d8d4b813d96880" + }, + "test-structure": { + "path": "1/test-structure.npz", + "sha256": "1ad63654994b3fdc6aad9e318410b8df8bf5bcd39c04a28db640b4059e7f454b" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 2, + "artifacts": { + "worker-input": { + "path": "2/worker-input.npz", + "sha256": "074688f4eb2b2a68e812f8af1768364d0dc2ca08a9962f930076c9d1e8e5d9ef" + }, + "train-rows": { + "path": "2/train-rows.npz", + "sha256": "edf5a2d705375a06bdc9ef06e647ea150dc099c2ff44139e7b0675d3a7e0d1a0" + }, + "validation": { + "path": "2/validation.npz", + "sha256": "bb1b0fe4e32d296a92a0d8971aa2ae88d16f766e39868e475c93c7ce50d83aec" + }, + "test-features": { + "path": "2/test-features.npz", + "sha256": "ca23ff9eea4c59b9acfc2fd36fa9b3404b908fae1a566a9f1d06673b84215478" + }, + "test-truth": { + "path": "2/test-truth.npz", + "sha256": "17ad5fce88828052c181bdaf81a8022bc0201fdb7952015bf9df16953cf693ab" + }, + "validation-structure": { + "path": "2/validation-structure.npz", + "sha256": "a9c070ca3d998086e4a02acc40d0953430edb43934db4f649aca2a756bcf6360" + }, + "test-structure": { + "path": "2/test-structure.npz", + "sha256": "c25925aa72084a9b3d9d62285ca793a49bf820333c2f4993734caa4a3fc33302" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 3, + "artifacts": { + "worker-input": { + "path": "3/worker-input.npz", + "sha256": "1722d246878ffead019d3d9b94e7343325c9f162c1a565ddd95664d6dd37f959" + }, + "train-rows": { + "path": "3/train-rows.npz", + "sha256": "bf38782ee6b21b3748ab91c048be51663bc2517dd4734cee8b9d5932b6a49a7f" + }, + "validation": { + "path": "3/validation.npz", + "sha256": "9b102f96fe81c4177da8c2ab0d0bf17107f80576544dc6337bfe1af978974871" + }, + "test-features": { + "path": "3/test-features.npz", + "sha256": "9c07a03bd91275e86df11fea4f2c835261a305b626a76a3b10a4621c979ed877" + }, + "test-truth": { + "path": "3/test-truth.npz", + "sha256": "3cdf2601b954023779c32ab58401241c201cc0bfb4a3951f99ced2c0dd3609cf" + }, + "validation-structure": { + "path": "3/validation-structure.npz", + "sha256": "7e5be679ee51be580728d0b93086ba61de01c61badb36f8a74f08636dc1e95bf" + }, + "test-structure": { + "path": "3/test-structure.npz", + "sha256": "ada5a1c15447085d3f249c39a17b8df6dd04b2bc507d97dec03cef434c3d5dde" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + }, + { + "seed": 4, + "artifacts": { + "worker-input": { + "path": "4/worker-input.npz", + "sha256": "c82bdfd8634be906a9717cfe1d17175519311ad4b262c4b5ef9a30ef4b04abce" + }, + "train-rows": { + "path": "4/train-rows.npz", + "sha256": "5d8f14f0b1648ea0d8322d6a1114cf1745367f6ba0f17f3268fd68620dc7e370" + }, + "validation": { + "path": "4/validation.npz", + "sha256": "a8a6d3bd7f7844e4829cca0761b8085d3d352b3ef1cba89663cf22577a43d8a2" + }, + "test-features": { + "path": "4/test-features.npz", + "sha256": "6b266d810547087863bcd3e9c03ab123b549e461f8a4f02cc19ddafea08cab0d" + }, + "test-truth": { + "path": "4/test-truth.npz", + "sha256": "d2423a1c9cafa045ea935f6fc952e037402f8c21cb660ab2e0a8ac412d67150f" + }, + "validation-structure": { + "path": "4/validation-structure.npz", + "sha256": "525e5e63ecc36671289b3a60d4b3efecf69f6cdfc203db5dc8b34cc8de26717b" + }, + "test-structure": { + "path": "4/test-structure.npz", + "sha256": "e6a7b82c20849d16220050d3112a342e460a81f12e248216a0b112280c0a6804" + } + }, + "metadata": { + "age_train_range": [ + 0.03571428571428571, + 13.035714285714286 + ] + } + } + ], + "source_freeze_sha256": "094049cfe8a9137b02a2599e6be823d669f191d31828989a9dae46797ef6e439", + "preprocessing_freeze_sha256": "d968104b972ea9f3575db9e2fbf9cd81f67a1deb5f70c65f93724889f159f4a9", + "adapter_sha256": "f5d7c152bddb250a8e0f42243d033aa5380ef9ff31f15455ef85892bb5a5f684", + "scope": "Five frozen folds; validation worker input includes early-stopping labels. Test files require evaluation-side custody, not enforced by this exporter." + } + }, + "cells": [ + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A1/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0882365420111455, + "artifacts": { + "model.bin": "4c9b8edf702331acae13bb5cd4af0515b01bd3d202285bf06ccddb2b98c47453", + "training.json": "3e29092b73a92c2315407cd4fc9ac67e7f6a32994b3c11e908694e71071461e0", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "5e3f564622e1e1a04f6beb989eb2445990225260115d0a0348898cd9d490dd7e" + }, + "application": "A1", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.1040290366770373, + 1.0606887101824045, + 1.0218986555332432, + 0.9879878092935267 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A1/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0455147080065217, + "artifacts": { + "model.bin": "5866ba1b83ae26aabdf80b27fae6fe2f0385a74fc90360d39d0ab0caf2c2176c", + "training.json": "716f5d21daf8b657d16f8938b3e15d165a75b94ca178d61cd51af475be688c66", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "bcb8a0912f960370e31674938e6e4fe96805ed9b9217162c910549c89a7fc0cf" + }, + "application": "A1", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.12648011579946, + 1.082676299088048, + 1.0438404198892604, + 1.0100368469056356 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A1/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0493324160051998, + "artifacts": { + "model.bin": "765c20ba319c745af2f6c8ca4c1e6f5c26d8db8d973059f1a21ab8a4d27e5ff2", + "training.json": "1dce227f2df0b32b3de1f54ac36ed55615084f217f104b0076343494951d0ae2", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "98334943d25e80cf0f26f6abae07f1ed51927664f01ecf0dcff9adad04fe392c" + }, + "application": "A1", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.1225223244307807, + 1.0803266948728714, + 1.0439130513244994, + 1.0109379748479417 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A1/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.111300125005073, + "artifacts": { + "model.bin": "70076bffcac38347538a94d97d799c2f28e50a3bb49bb460519796231ee65c5f", + "training.json": "8def85925bec9f9ac860022202007f4c8cda761e54c8a3f67644cb92822a5655", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1a04b2817da59b47cfad89fa171a9bb15a405370b5b27d4f2fa1965babe299d5" + }, + "application": "A1", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.094678299070021, + 1.0522510401996432, + 1.0156013308134624, + 0.9829172497917497 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A1/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0606220420013415, + "artifacts": { + "model.bin": "412e929cdefe538df673901e86fe96b310ec14fface97d4d578cfbe4c399f4af", + "training.json": "9d3a6ccd3732f117ecce3e3223271650d96e3981abb7be512fc502740e04048d", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "072d7f0efc9f4ef07d9927df274abc922937bccc3cb6f4120c139c5a9f314b39" + }, + "application": "A1", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.1138331229959961, + 1.0724223160201456, + 1.036958391075404, + 1.004141627720936 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A6/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0556113329948857, + "artifacts": { + "model.bin": "c6c7a34fb41b61428e0fd6bc8c483e2bf6e9b2ac71325ba3a1354d70f9fa039a", + "training.json": "6788d336be85fb02678adbb24b58498951d6d9942750dfaf825adf248de97944", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "27c9235cf62fc2ba3ddf30241c590e3f5520b767e905c009a382aa7b19a4d856" + }, + "application": "A6", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 3, + "stopping": [ + { + "selected_rounds": 3, + "history": { + "validation": { + "rmse": [ + 1.0152097818097066, + 1.0090900204206783, + 1.006988270744545, + 1.0101328869699957 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.832476455405597, + 29.161076799541192 + ], + "std": [ + 8.446609478513288, + 10.891162854284502 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A6/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0537466669920832, + "artifacts": { + "model.bin": "6938041169bca284904143711e3aaab66a18021b50ef37be529b3794e97634a6", + "training.json": "1aa3b5436227d077e75aa4447aebbecf32099780dd1d20b812b209519578b5b9", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "753c256e35fa2117e0e66b0a59e2c642fe06269a887d1320acb40101464a6e61" + }, + "application": "A6", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 0.9064281668733252, + 0.8914249917534706, + 0.8808437264937701, + 0.8737753750518703 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.02220744409841, + 28.889462439852874 + ], + "std": [ + 8.707706178768547, + 11.553396813667037 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A6/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0467143749992829, + "artifacts": { + "model.bin": "a38960fb0e4bfb7f02ce9ca3f0856ce7dc028c547b3d0a34ad4591863f5345ec", + "training.json": "2fb6802d3a392be052c9df7cb4366dabea48e99e2810593ce8bc5fe5964f9509", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "24dfcd1ded2e27dbb0fb791bce1b41bc39aafced7fe16c10fcc974a2a3597b6b" + }, + "application": "A6", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.2712642722409764, + 1.2355390257974619, + 1.2084964496812456, + 1.1808598831164345 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 22.402470691547656, + 31.08271761800212 + ], + "std": [ + 7.461557603692726, + 9.80291946506434 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A6/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.051978165996843, + "artifacts": { + "model.bin": "58c07894178a22083424340ba0030405117f19fbac3a20f3d21c746732446f3b", + "training.json": "cdedbd0c3aee5d907dc8183f8d4d2c024c2798b2dac700dd965d595bd83ee256", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "39037007c526321da71f584e6a6256ae6d5d8230eed16b08849d44d6855bccc7" + }, + "application": "A6", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 2, + "stopping": [ + { + "selected_rounds": 2, + "history": { + "validation": { + "rmse": [ + 0.8482051365361111, + 0.8478712494939465, + 0.8498507877363486, + 0.8534675082907229 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.36469700842268, + 28.64957766482703 + ], + "std": [ + 8.097653633950642, + 10.41318564767784 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A6/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0611249579960713, + "artifacts": { + "model.bin": "788ad42ae7ff3581706a62c8b30c3cbcb29370af892f813f8f8661bcabe8d4e7", + "training.json": "d43f28849e87ddc9f5a5cae0f15f66456251c35928bc9da1375be2716c4249c6", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1e818da9f56f12e88b87b2873cf6eb4e66dfc1c651d9c761357288a6d896761a" + }, + "application": "A6", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 1.3698645536405305, + 1.3439158521723147, + 1.3229687919719846, + 1.300697571329999 + ] + } + } + } + ], + "target_scale": { + "mean": [ + 21.608206573705065, + 28.812343511667468 + ], + "std": [ + 7.37511081258478, + 9.516732486364944 + ], + "constant": [ + false, + false + ] + } + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A11/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.5218369579961291, + "artifacts": { + "model.bin": "9551fa2cd237565d8e30afe9f13b38150796464459dba2f6383da2b04fc1333f", + "training.json": "5828526feff7915a44761f6a214c2e44e85c2e1341e13a0b196e020507ba95a3", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "38c336c83d00b67d3d32fa234c99f1b2b3b6d7f17d60848a1a4bf37ab8265290" + }, + "application": "A11", + "fold": 0, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5222230477926284, + 1.480984884040511, + 1.4448661314897857, + 1.4192193388805154 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5153939906237581, + 1.4730535147673454, + 1.4368484205236978, + 1.4103263868666862 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A11/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.46515679100411944, + "artifacts": { + "model.bin": "1495d57a0713df0ac2b4505c7cc0cc964d7b5220bcec0b5dd6cc7cfe6bd1e81c", + "training.json": "adeb392e9d71a5884063ddc9b2c9054bbf859900c3a423bb86dad2d7ede0e87c", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "7b6dfac20f0e618c22a0b10b82076c8d7b24bd85145802cbafae51aa22f9add0" + }, + "application": "A11", + "fold": 1, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5049282105546857, + 1.463238032836992, + 1.4314289942641896, + 1.398575327192213 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5327034390677288, + 1.4874700681826045, + 1.453744239948275, + 1.4199022988363361 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A11/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.4565855419932632, + "artifacts": { + "model.bin": "c887a6af5b303121137cfef5818605bc50cb15a4fa0e9245e7e06bd052d05c32", + "training.json": "38c1ea43b51b5d620038e7afc2243412e802cd706f6075330764c9fe82d76621", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "0e3cd067a7312b318f96f2f487ee799747f09f0f763b1a2ab9c7e37d7e7f093c" + }, + "application": "A11", + "fold": 2, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5217132116875307, + 1.4897635505530253, + 1.4517624866431436, + 1.4195040146834654 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5388203405622187, + 1.5057508976405607, + 1.467325684201917, + 1.4351657947544998 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A11/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.45408466600929387, + "artifacts": { + "model.bin": "4bf475738900e5448a3dce0feb11764d2c3b0af6d8ca7b065ce3b87f2c5e691d", + "training.json": "1ab99c8f2084b3dcdc3533da59f7a4c829804afa363754636f9296b985c9f65a", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "eeb9ea8f14964e33fb6b2ae1e075f97a0297a518bf04a1f488e64b69a6a8aeb5" + }, + "application": "A11", + "fold": 3, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5192109500257958, + 1.477542273093424, + 1.442107020019317, + 1.4086702171512198 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5074776988095129, + 1.4667578450478718, + 1.4309549264552512, + 1.3976966352452378 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A11/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 0.46762545900128316, + "artifacts": { + "model.bin": "f940ca2303b9b653df0a1defeb77f0fe80c7583f9307cf01195b9f7a405ef56b", + "training.json": "73853c9d18517f3a9ba21f8bd3cc00f753f64bff8229ac93f94314d6f02a5444", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "d54e202f5cdcf077cb4f0e8e7b271d98c3cb664a2d32df0d1f6d3e988f934512" + }, + "application": "A11", + "fold": 4, + "library": "catboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": null, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.5230454263784095, + 1.4898330592814988, + 1.4545513374681012, + 1.4206380657402569 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.5265866383619924, + 1.4950096984945573, + 1.4605720090766612, + 1.427506882477277 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A12/0/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0515962909994414, + "artifacts": { + "model.bin": "0bd0f724c90b61cdff6c3c4c20467067ce734ad738dbd3ea4711395dbba8bf58", + "training.json": "8acf5a5cd21a0d056c868e26de99f3e02c0adf5abd526826c429a1c4eac82260", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6ca4bf3a15a9d2f7e67e48363d7f4d6e61408e3ef76e248aaaccec72b9e5d2f4" + }, + "application": "A12", + "fold": 0, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 14.893546275317354, + 14.310820386115674, + 13.683061806840724, + 13.249780511152489 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A12/1/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0540498749905964, + "artifacts": { + "model.bin": "232d17c42e4f476a2c78e59f4003b21afa6312f7ffb9892ec248cbde1548d75c", + "training.json": "00c9d4688d917c7a1b95b4324b5c053735108bc593a009c2bb901275bd14053e", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "f710c2b4fd4b6f1a9478ba5384b9946b1efd74ba239699a20ffd4389347de500" + }, + "application": "A12", + "fold": 1, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.711770268699254, + 15.051416652146338, + 14.554739095401072, + 14.05666347254798 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A12/2/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0541163329908159, + "artifacts": { + "model.bin": "b68fdfdb89a7c17d13d326254858d13ffbc1496b9a8aa6734056473dff77fefa", + "training.json": "01dce934ac3b346d7dcf15c1ebf8fca900c397196e248871d5f57fb73ce99bf2", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "1e504a639102f5a58cccc22d8b524a25f9cde5dd52fc2f32cc05843912db5de0" + }, + "application": "A12", + "fold": 2, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.19329904042284, + 14.609148043942902, + 14.059621503399761, + 13.46748393578638 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A12/3/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0577677919936832, + "artifacts": { + "model.bin": "63dc33ae3c00fec2d04ae056dd5ce01ff84ab4761c4ab3a3ce4aeef15118abda", + "training.json": "b956a07dc106e29abb2c3930616de06e85781344cfce0f719965adc710743242", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "57951079cbdda8d5fc37d7219dff502a9b6d35ae759ae4be3e76f6fcced645d2" + }, + "application": "A12", + "fold": 3, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.652135794564133, + 15.083851955094127, + 14.477455842030803, + 13.985865542481408 + ] + } + } + } + ], + "target_scale": null + } + }, + { + "command": [ + "/Users/jiaruixu/work_space/openboost/build/v1-env/bin/python", + "/Users/jiaruixu/work_space/openboost/benchmarks/v1/baseline_worker.py", + "/Users/jiaruixu/work_space/openboost/build/v1-worker-data-smoke-003/A12/4/job.json" + ], + "timeout_s": 90, + "threads": 2, + "status": "pass", + "exit_code": 0, + "reason": "", + "wall_s": 1.0517872089985758, + "artifacts": { + "model.bin": "24a41e33b1b4f6f92b95c82fdeab573c5837e0cfa11d75a39ebea30635b88382", + "training.json": "bdbdc8b3661dac2a67da7e74a7e2adf5b94e0c6b80b920a3c4167f69cf685ff8", + "worker.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "predictions.npz": "6b05f7435a5ecf641ceb64c90d999e3ec9e37ef1c9ee2d678c9a47706de3c5ac" + }, + "application": "A12", + "fold": 4, + "library": "xgboost", + "training": { + "early_stopping_rounds": 3, + "prediction_rounds": 4, + "stopping": [ + { + "selected_rounds": 4, + "history": { + "validation": { + "rmse": [ + 15.101568342453419, + 14.48434154845502, + 13.894099076194196, + 13.341288656055848 + ] + } + } + } + ], + "target_scale": null + } + } + ] +} diff --git a/benchmarks/v1/evidence/recipe-results-042/README.md b/benchmarks/v1/evidence/recipe-results-042/README.md new file mode 100644 index 0000000..c902a9d --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/README.md @@ -0,0 +1,20 @@ +# Installed structural-result and scheduling evidence + +Parent b99376d plus the dirty source changes recorded by the wheel/source hashes. +This foundation revision resolves Sprint 041's rejected OrderedResult; the older +failure artifact remains intact. No extension algorithm source change was needed. + +- scheduler-checks.json: M=1/8/32 built-in/ordered runs, expected state identities, + stop rounds, predictions, and failure/reorder/regroup/retry checks. +- ordered-checks.json: six ordered cases now match direct and scheduled execution. +- ordered-expected.json: independent mathematical reference traces. +- d2.json, d3.json, ordered-0.json through ordered-5.json: saved raw inference models. +- checks.json: rerun D2/D3 mathematical checks and predictions. +- manifest.json: source/reference/wheel/artifact hashes, environment, exact + commands/cwds and installed verification status. + +Reproduce with `uv run --no-sync python examples/v1_extensions/verify.py OUTPUT_DIR` +using cached offline dependencies, Python 3.12 and NumPy 2.3.5. The disposable +environment is removed; absolute command-log paths identify this particular run. +These are deterministic sequential development checks, not formal E5, full D5 +author evidence, batching performance, real selection quality or adoption. diff --git a/benchmarks/v1/evidence/recipe-results-042/checks.json b/benchmarks/v1/evidence/recipe-results-042/checks.json new file mode 100644 index 0000000..2213353 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/checks.json @@ -0,0 +1,93 @@ +{ + "values": [ + [ + 0.0, + "a" + ], + [ + 1.0, + "b" + ], + [ + 2.0, + null + ], + [ + 3.0, + "a" + ], + [ + null, + "b" + ], + [ + 5.0, + "a" + ] + ], + "row_ids": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "names": [ + "x", + "category" + ], + "kinds": [ + "numeric", + "categorical" + ], + "predictions": { + "d2": [ + [ + 1.5801874999999999 + ], + [ + 1.5801874999999999 + ], + [ + 1.7251249999999998 + ], + [ + 2.106375 + ], + [ + 2.7743 + ], + [ + 2.7743 + ] + ], + "d3": [ + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.092 + ], + [ + 5.164000000000001 + ] + ] + }, + "d2_cuts": [ + 1, + 1, + 1 + ], + "d3_max_absolute_error": 8.881784197001252e-16, + "rounds": 3 +} diff --git a/benchmarks/v1/evidence/recipe-results-042/d2.json b/benchmarks/v1/evidence/recipe-results-042/d2.json new file mode 100644 index 0000000..b6d4143 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/d2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [2.25], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.0], [2.0]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [126, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0], [-2.2874999999999996], [1.5249999999999997]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, 0, -1, -1, -1], "threshold": [190, 63, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.008472222222222017], [-1.7332499999999995], [1.718], [-2.410625], [-0.9612499999999998]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/d3.json b/benchmarks/v1/evidence/recipe-results-042/d3.json new file mode 100644 index 0000000..5efc3b8 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/d3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["x", "category"], "base": [4.0], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.76], [3.76]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["x", "category"], "cuts": [[0.015748031496062992, 0.031496062992125984, 0.047244094488188976, 0.06299212598425197, 0.07874015748031496, 0.09448818897637795, 0.11023622047244094, 0.12598425196850394, 0.14173228346456693, 0.15748031496062992, 0.1732283464566929, 0.1889763779527559, 0.2047244094488189, 0.2204724409448819, 0.23622047244094488, 0.25196850393700787, 0.2677165354330709, 0.28346456692913385, 0.2992125984251969, 0.31496062992125984, 0.33070866141732286, 0.3464566929133858, 0.36220472440944884, 0.3779527559055118, 0.3937007874015748, 0.4094488188976378, 0.4251968503937008, 0.4409448818897638, 0.4566929133858268, 0.47244094488188976, 0.4881889763779528, 0.5039370078740157, 0.5196850393700787, 0.5354330708661418, 0.5511811023622047, 0.5669291338582677, 0.5826771653543307, 0.5984251968503937, 0.6141732283464567, 0.6299212598425197, 0.6456692913385826, 0.6614173228346457, 0.6771653543307087, 0.6929133858267716, 0.7086614173228346, 0.7244094488188977, 0.7401574803149606, 0.7559055118110236, 0.7716535433070866, 0.7874015748031497, 0.8031496062992126, 0.8188976377952756, 0.8346456692913385, 0.8503937007874016, 0.8661417322834646, 0.8818897637795275, 0.8976377952755905, 0.9133858267716536, 0.9291338582677166, 0.9448818897637795, 0.9606299212598425, 0.9763779527559056, 0.9921259842519685, 1.0078740157480315, 1.0236220472440944, 1.0393700787401574, 1.0551181102362204, 1.0708661417322836, 1.0866141732283465, 1.1023622047244095, 1.1181102362204725, 1.1338582677165354, 1.1496062992125984, 1.1653543307086613, 1.1811023622047243, 1.1968503937007875, 1.2125984251968505, 1.2283464566929134, 1.2440944881889764, 1.2598425196850394, 1.2755905511811023, 1.2913385826771653, 1.3070866141732282, 1.3228346456692914, 1.3385826771653544, 1.3543307086614174, 1.3700787401574803, 1.3858267716535433, 1.4015748031496063, 1.4173228346456692, 1.4330708661417322, 1.4488188976377954, 1.4645669291338583, 1.4803149606299213, 1.4960629921259843, 1.5118110236220472, 1.5275590551181102, 1.5433070866141732, 1.5590551181102361, 1.5748031496062993, 1.5905511811023623, 1.6062992125984252, 1.6220472440944882, 1.6377952755905512, 1.6535433070866141, 1.669291338582677, 1.68503937007874, 1.7007874015748032, 1.7165354330708662, 1.7322834645669292, 1.7480314960629921, 1.763779527559055, 1.779527559055118, 1.795275590551181, 1.811023622047244, 1.8267716535433072, 1.8425196850393701, 1.858267716535433, 1.874015748031496, 1.889763779527559, 1.905511811023622, 1.921259842519685, 1.937007874015748, 1.952755905511811, 1.968503937007874, 1.984251968503937, 2.0, 2.015748031496063, 2.031496062992126, 2.047244094488189, 2.062992125984252, 2.078740157480315, 2.094488188976378, 2.1102362204724407, 2.125984251968504, 2.141732283464567, 2.15748031496063, 2.173228346456693, 2.188976377952756, 2.204724409448819, 2.220472440944882, 2.236220472440945, 2.251968503937008, 2.267716535433071, 2.283464566929134, 2.2992125984251968, 2.3149606299212597, 2.3307086614173227, 2.3464566929133857, 2.3622047244094486, 2.377952755905512, 2.393700787401575, 2.409448818897638, 2.425196850393701, 2.440944881889764, 2.456692913385827, 2.47244094488189, 2.4881889763779528, 2.5039370078740157, 2.5196850393700787, 2.5354330708661417, 2.5511811023622046, 2.5669291338582676, 2.5826771653543306, 2.5984251968503935, 2.6141732283464565, 2.62992125984252, 2.645669291338583, 2.661417322834646, 2.677165354330709, 2.6929133858267718, 2.7086614173228347, 2.7244094488188977, 2.7401574803149606, 2.7559055118110236, 2.7716535433070866, 2.7874015748031495, 2.8031496062992125, 2.8188976377952755, 2.8346456692913384, 2.8503937007874014, 2.8661417322834644, 2.8818897637795278, 2.8976377952755907, 2.9133858267716537, 2.9291338582677167, 2.9448818897637796, 2.9606299212598426, 2.9763779527559056, 2.9921259842519685, 3.015748031496063, 3.047244094488189, 3.078740157480315, 3.1102362204724407, 3.1417322834645667, 3.1732283464566926, 3.2047244094488185, 3.2362204724409445, 3.2677165354330713, 3.299212598425197, 3.330708661417323, 3.362204724409449, 3.393700787401575, 3.425196850393701, 3.456692913385827, 3.4881889763779528, 3.5196850393700787, 3.5511811023622046, 3.5826771653543306, 3.6141732283464565, 3.6456692913385824, 3.6771653543307083, 3.7086614173228343, 3.74015748031496, 3.771653543307087, 3.803149606299213, 3.834645669291339, 3.866141732283465, 3.8976377952755907, 3.9291338582677167, 3.9606299212598426, 3.9921259842519685, 4.0236220472440944, 4.05511811023622, 4.086614173228346, 4.118110236220472, 4.149606299212598, 4.181102362204724, 4.21259842519685, 4.244094488188976, 4.275590551181103, 4.307086614173229, 4.338582677165355, 4.3700787401574805, 4.4015748031496065, 4.433070866141732, 4.464566929133858, 4.496062992125984, 4.52755905511811, 4.559055118110236, 4.590551181102362, 4.622047244094488, 4.653543307086614, 4.68503937007874, 4.716535433070866, 4.748031496062992, 4.7795275590551185, 4.811023622047244, 4.84251968503937, 4.874015748031496, 4.905511811023622, 4.937007874015748, 4.968503937007874], []], "categories": [null, ["a", "b"]], "feature": [0, -1, -1], "threshold": [190, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[3.5200000000000005], [3.58], [3.9400000000000004]]}, "mapping": [[1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/manifest.json b/benchmarks/v1/evidence/recipe-results-042/manifest.json new file mode 100644 index 0000000..10cf787 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/manifest.json @@ -0,0 +1,211 @@ +{ + "schema": "openboost-v1-development-extensions-v1", + "passed": true, + "claim": "repository-authored D2/D3/D4 and D5 scheduling development checks only", + "commit": "b99376d9ab912c9c9293f4ad29eb9ad43f262d61", + "dirty": true, + "os": "macOS-26.3-x86_64-i386-64bit", + "python": "3.12.12", + "machine": "x86_64", + "cpu_count": 16, + "device": "cpu", + "threads": 1, + "commands": [ + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/cohort_splits" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/penalized_leaves" + }, + { + "argv": [ + "uv", + "build", + "--wheel", + "--offline", + "--out-dir", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels" + ], + "cwd": "/Users/jiaruixu/work_space/openboost/examples/v1_extensions/ordered_updates" + }, + { + "argv": [ + "uv", + "venv", + "--python", + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "uv", + "pip", + "install", + "--offline", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "numpy==2.3.5", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels/ob_cohort_splits-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels/ob_ordered_updates-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels/ob_penalized_leaves-0.1.0-py3-none-any.whl", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/wheels/openboost-1.0.0.dev0-py3-none-any.whl" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/checks.py", + "/private/tmp/openboost-v1-results-042" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "/Users/jiaruixu/work_space/openboost/.venv/bin/python3", + "-m", + "examples.v1_extensions.ordered_oracle", + "/private/tmp/openboost-v1-results-042/ordered-expected.json" + ], + "cwd": "/Users/jiaruixu/work_space/openboost" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/ordered_checks.py", + "/private/tmp/openboost-v1-results-042" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/scheduler_checks.py", + "/private/tmp/openboost-v1-results-042" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "-I", + "-c", + "import json,importlib.metadata as m; print(json.dumps({n:m.version(n) for n in ['openboost','numpy','ob-cohort-splits','ob-penalized-leaves','ob-ordered-updates']}))" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "uv", + "pip", + "uninstall", + "--python", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "ob-cohort-splits", + "ob-penalized-leaves", + "ob-ordered-updates" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + }, + { + "argv": [ + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/env/bin/python", + "-I", + "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n/core_inference.py", + "/private/tmp/openboost-v1-results-042" + ], + "cwd": "/var/folders/4r/kxlh5m6j0f16tcx91xd5sgv40000gn/T/openboost-v1-extensions-vwd9_28n" + } + ], + "sources": { + "examples/v1_extensions/checks.py": "47556dd3e9b5681d1bddb0ae8e086748fe273ddf39e25ab35b5e6a27d34e985b", + "examples/v1_extensions/cohort_splits/pyproject.toml": "d29f65c7783437a6220ec2dcfc6c4497792e804226c2b8f55d33f25560267245", + "examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py": "013730f77db77255d3be58ce16fe4883e55e4dcea80ac757a681fa5444e82178", + "examples/v1_extensions/core_inference.py": "b116de8066a459000fb4a134a9fa520d6a76dd7228c2fa2091731fa52935ff70", + "examples/v1_extensions/ordered_checks.py": "f431ffcfa1f578ce1ca1d9dece98efcc3ca239d881ac62ff257b53c7e6e265f9", + "examples/v1_extensions/ordered_oracle.py": "f225d630f3ec84645557b1ccc99839864393a1b7c1032ce55f346d2ff2641fe1", + "examples/v1_extensions/ordered_updates/pyproject.toml": "6e3558622d940f91f6454483b962957d218001575dcce5e46172be7769c2c28e", + "examples/v1_extensions/ordered_updates/src/ob_ordered_updates/__init__.py": "4b71fcfba6ccb207f98f10ed765fe0eaa6f8097e678972efd831927ddc8fdcf2", + "examples/v1_extensions/penalized_leaves/pyproject.toml": "e00f1e32176ca2f5295e233c2421c39f729babfea0d283228896b53fd914e4fe", + "examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py": "54208a6c52fe2acacc539f25428f31e28303a286c50dd5892b403c08e1fa0be2", + "examples/v1_extensions/scheduler_checks.py": "ea8bf14b9f046e2b65c04bfe096f82e054c219f0b440315ef65cfa0ec00f0599", + "examples/v1_extensions/verify.py": "ae32b15b18f3e797ab520e2c96253e4fcf5c55445e36ebba791aa56d29be4089" + }, + "reference_sources": { + "tests/v1/reference/__init__.py": "a6b57448d0bd41863e7d88c7f79a0429652b399ffa8a45478b948cd38f7c57c7", + "tests/v1/reference/author.py": "155d6b2a02baaed5357a1032227e263a6c53d5ee59e6cd6c700f24b13d487dac", + "tests/v1/reference/classification.py": "f3a9cfd5610cef1e2b05cf8e4b2eaf0b77eee5ee12d57d8bdf1c0195613900ad", + "tests/v1/reference/coupled.py": "691c9bd8dacc645d824d7c75bd909590095b07844b923a6929ac79bb8f7e5a8b", + "tests/v1/reference/data.py": "10e4380b35fa156cc6a1c025bca172733289c032b6c4c06421e11e9d430be072", + "tests/v1/reference/integration.py": "62ba6d983b5b457690272d4e28e5b179f6732e0a78ab9faa3ea176fa9119d27e", + "tests/v1/reference/mixed.py": "70dfce643f1b4b6e8d6014c642efbae0d10d32b7269706867af2c590ae84641e", + "tests/v1/reference/positive.py": "d55c40f5dfd96aa58e4ccdb02cb9f7691352e82966bccb4e13d79e278c6ba190", + "tests/v1/reference/quantile.py": "6e05e309c06494cde68d4c11496b7d7aad7f0c4958e0688562fc79451855079a", + "tests/v1/reference/ranking.py": "a2d23b4ba1595b8aac90cdc32b0e41fa90fc42e11a999019d53405eb62199a0c", + "tests/v1/reference/runs.py": "010c43f3752bd22d4959ec89ea40de8f7705ec116dfb53ccae7a3e85c3c93c20", + "tests/v1/reference/scalar.py": "5d78f361abb2bdad2532e322f2b2138ee7c5bd6fed55d6106a63ba707cadb6b1", + "tests/v1/reference/survival.py": "1b4d6704edc2b58c2ddb39b6e4f00de9d205c54250c2a95a8a0458cdab01f46a", + "tests/v1/reference/tree.py": "d8afa1a3ea9447f2fa420317e972e5b2e42540888ce42106d69ccd61aae21874", + "tests/v1/reference/vector.py": "1294d8c452df385c0c31f15c9dd57e11c09ae17bef62d0fa8f5754c4e7efe12f" + }, + "wheels": { + "ob_cohort_splits-0.1.0-py3-none-any.whl": "2e9ebb56cd551f6ebeb150e866c5107f508d62069c45841ffdf0e51c6305141b", + "ob_ordered_updates-0.1.0-py3-none-any.whl": "9a058e89931a5c820350f6b34ccc541c0be86e0becc06d1c14a7b1f943b6c310", + "ob_penalized_leaves-0.1.0-py3-none-any.whl": "e7723fc496bcaf908b52f6ed11483833bbdb1f51da7c5e02eead10d27ed90236", + "openboost-1.0.0.dev0-py3-none-any.whl": "eb7246deab69de5cbdf7626bcc9993e09b605c73e3d65b74c996d3abfb1e3902" + }, + "versions": { + "openboost": "1.0.0.dev0", + "numpy": "2.3.5", + "ob-cohort-splits": "0.1.0", + "ob-penalized-leaves": "0.1.0", + "ob-ordered-updates": "0.1.0" + }, + "plugin_free_inference": true, + "artifacts": { + "checks.json": "184006082086b9b5593ea7a7e18c9fdbd34ecae7beb52fa72ac64b883ffd045c", + "d2.json": "facc6da0ae50d74322f4552cf70a11c749f480a7dfb7d102579cec825e88e657", + "d3.json": "f701c628ebbf191056b72bb2a1d3b92d33385189423fa53bc609b33107f803f2", + "ordered-0.json": "325017af3b8f819664eff01df2ac49cfeadc7b042bba416cdd378c3b30016a04", + "ordered-1.json": "03cc3d28f8adc851a28f9596af22376026d8b81b3cce5a126f3ed1c2ea74576e", + "ordered-2.json": "d8b26886a41ee3b194c6983fc2f8b905b741133ef228ffa28fe8f604fd6bba2f", + "ordered-3.json": "35a29588374bfb82a559c42c3a91b67b8fed43a1778610c6078b1c1eda2103d9", + "ordered-4.json": "7ca0acb133c1c034f67f116b58cd4c836ae8c8e67ba31d9656d15c01d90c9de9", + "ordered-5.json": "22067f5ed6ead94e721bf03d652edbe9b3faa3c8c62f08ce85b8f57d12079ac2", + "ordered-checks.json": "998b3a0e2df12fa149c26099753687735d1bcbd7060db8f426c4c87cde9659d0", + "ordered-expected.json": "68f858fd4edcc561b16c492b146f3f28cc079212797cff6334493893cd499926", + "scheduler-checks.json": "7840dc5e6d1d7bbbecafb0ab883078bbb3868e4afbdc603c89e652299f60df1e" + } +} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-0.json b/benchmarks/v1/evidence/recipe-results-042/ordered-0.json new file mode 100644 index 0000000..3f722f0 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-0.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.0], [-1.55], [1.55], [-1.734375], [-0.40625]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.05211814894448784], [0.537839591477717], [-0.2975117307047917]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00656250000000015], [-1.4378125000000002], [1.4259999999999997], [-1.6042968750000002], [-0.38593750000000004], [2.01625], [0.7743749999999998]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.11905690030262675], [0.37209581591266455], [-0.3125427929230907]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0029869791666665454], [-1.3338359375], [1.3392124999999997], [-1.483974609375], [-0.36664062500000005], [1.9154375], [0.7162968749999997]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.1674487489868146], [0.2577416257105913], [-0.3257519454304436]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-1.json b/benchmarks/v1/evidence/recipe-results-042/ordered-1.json new file mode 100644 index 0000000..7e2823f --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-1.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-2.4671622769447922e-17], [0.617670054730258], [-0.26471573774153917], [-0.11415168100078188], [-0.30211102423768577]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.0], [-1.55], [1.55], [-1.734375], [-0.40624999999999994]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.08185660294329283], [0.4366019642073911], [-0.29235933130168695]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[-0.00656250000000015], [-1.4378125000000002], [1.4259999999999997], [-1.6042968750000002], [-0.38593750000000004], [2.01625], [0.7743749999999998]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.1386300959880271], [0.30287104064812337], [-0.3080405694052306]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, 0, -1, -1, -1, -1], "threshold": [4, 2, 0, -1, -1, -1, -1], "missing_left": [false, false, true, false, false, false, false], "left": [1, 3, 5, -1, -1, -1, -1], "right": [2, 4, 6, -1, -1, -1, -1], "value": [[0.0029869791666665454], [-1.3338359375], [1.3392124999999997], [-1.483974609375], [-0.36664062500000005], [1.9154375000000001], [0.7162968749999996]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-2.json b/benchmarks/v1/evidence/recipe-results-042/ordered-2.json new file mode 100644 index 0000000..208f9e2 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-2.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[1.2335811384723961e-17], [-0.31024237685691947], [0.31024237685691947], [-0.3471462079749804], [-0.08131352619233777]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [0, -1, -1], "missing_left": [true, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.021532890322055052], [1.202805041760208], [-0.5431730197398742]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[-0.006713870020361525], [-0.29772120406858105], [0.2856362380319303], [-0.3272827548559434], [-0.08973750045956581]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.14554489709520965], [0.7794770933518844], [-0.5211907648446487], [-0.2136889306782752], [-0.601453712375543]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.004724115047481874], [-0.28996494829505315], [0.2814615412095858]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.2124232464137005], [0.551922150051749], [-0.5096536668397931], [-0.2094904804283043], [-0.5878208453187278]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-3.json b/benchmarks/v1/evidence/recipe-results-042/ordered-3.json new file mode 100644 index 0000000..c6c645b --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-3.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.8125, 0.8043281785496401], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-4.9343245538895844e-17], [1.235340109460516], [-0.5294314754830783], [-0.22830336200156376], [-0.6042220484753715]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[-0.0001705718713431972], [-0.2921712432158142], [0.29186421384739647]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.13678840055064134], [0.7962304698465822], [-0.5171124306422169], [-0.2224970798353251], [-0.5904591549979086]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.002177683397250046], [-0.28361181227925364], [0.28753164239430373]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, 0, -1, -1], "threshold": [0, -1, 2, -1, -1], "missing_left": [true, false, false, false, false], "left": [1, -1, 3, -1, -1], "right": [2, -1, 4, -1, -1], "value": [[-0.20521263860862954], [0.5620408713757129], [-0.5047194802292578], [-0.21693892691354133], [-0.5764439161728362]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, -1, -1], "threshold": [4, -1, -1], "missing_left": [false, false, false], "left": [1, -1, -1], "right": [2, -1, -1], "value": [[0.005841160761291825], [-0.2812088257653382], [0.29172291513566345]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-4.json b/benchmarks/v1/evidence/recipe-results-042/ordered-4.json new file mode 100644 index 0000000..27d6836 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-4.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.7901594196053434, 0.5413248546129181], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.7132126139527316], [-0.05361965178016352], [1.3374023568950804], [-0.09977238181478348], [0.06549563417915817]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5681877339281609], [-0.10366525878116846], [1.1264031798518581], [-0.16860988364095933], [0.07805662032899752]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6916677838393316], [-0.04950491988957833], [1.2945069308003752], [-0.0932129638142678], [0.06266362790458978]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5002017662617205], [-0.09741553384741679], [0.9977787131185136], [-0.1589450731540854], [0.07435131168962882]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6664451940126678], [-0.045701998802632156], [1.245303348025434], [-0.0871011118862839], [0.05994722676598742]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.44007587706961016], [-0.09152583965232537], [0.8836624183776237], [-0.14982120030930768], [0.07082780148780192]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-5.json b/benchmarks/v1/evidence/recipe-results-042/ordered-5.json new file mode 100644 index 0000000..6bb1eaa --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-5.json @@ -0,0 +1 @@ +{"format": "openboost-ensemble-v2", "feature_names": ["feature"], "base": [3.7901594196053434, 0.5413248546129181], "terms": [{"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5925937328319512], [-0.10515689902826628], [1.1718256181257787], [-0.17122868934608548], [0.07956513112150526]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.742570059559497], [-0.05001600877035171], [1.386642115977446], [-0.09451013935344513], [0.063980256781011]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.5195503218044729], [-0.09881503450919896], [1.0340056137572504], [-0.16141157957113175], [0.0757855728692661]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.7136045762384516], [-0.046162405522191106], [1.330650642751404], [-0.08830777468338843], [0.0612095355612991]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.4554821173230488], [-0.09283829703588686], [0.9127061082173746], [-0.15214372865535286], [0.07219171472098855]]}, "mapping": [[0.0, 1.0]], "coefficient": 0.1}, {"kind": "tree", "learner": {"format": "openboost-tree-v3", "feature_names": ["feature"], "cuts": [[0.6666666666666666, 1.3333333333333333, 2.0, 2.6666666666666665, 3.3333333333333335]], "categories": [null], "feature": [0, 0, -1, -1, -1], "threshold": [4, 2, -1, -1, -1], "missing_left": [false, false, false, false, false], "left": [1, 3, -1, -1, -1], "right": [2, 4, -1, -1, -1], "value": [[0.6819662495495865], [-0.04260017510397546], [1.2701394242932311], [-0.08252636533256741], [0.05855229290519618]]}, "mapping": [[1.0, 0.0]], "coefficient": 0.1}], "classes": null} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-checks.json b/benchmarks/v1/evidence/recipe-results-042/ordered-checks.json new file mode 100644 index 0000000..eae0b6e --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-checks.json @@ -0,0 +1,190 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "predictions": { + "ordered-0": [ + [ + 3.3302353515625, + 0.9210958818597373 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.6966171875000002, + 0.7107475316438076 + ], + [ + 4.1165671875, + 0.7107475316438076 + ], + [ + 4.36066875, + 0.9210958818597373 + ] + ], + "ordered-1": [ + [ + 3.3302353515625, + 0.9400424845082175 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.6966171875000002, + 0.7140770860551798 + ], + [ + 4.1165671875, + 0.7140770860551798 + ], + [ + 4.36066875, + 0.9400424845082175 + ] + ], + "ordered-2": [ + [ + 3.7160606088874024, + 1.0577486070660242 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7663984025053043, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 1.0577486070660242 + ] + ], + "ordered-3": [ + [ + 3.726800811873959, + 1.0636893236179212 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 1.0636893236179212 + ] + ], + "ordered-4": [ + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.8089700684903174, + 0.563648427963561 + ], + [ + 4.177880683177432, + 0.8421092857477177 + ], + [ + 4.177880683177432, + 0.8421092857477177 + ] + ], + "ordered-5": [ + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.8085336281300934, + 0.564079096484094 + ], + [ + 4.188902637907551, + 0.8531785886229586 + ], + [ + 4.188902637907551, + 0.8531785886229586 + ] + ] + }, + "versions": { + "ordered-0": 6, + "ordered-1": 6, + "ordered-2": 6, + "ordered-3": 6, + "ordered-4": 6, + "ordered-5": 6 + }, + "max_absolute_error": 2.220446049250313e-16, + "scheduler_status": "structural result accepted; all six cases match independent execution" +} diff --git a/benchmarks/v1/evidence/recipe-results-042/ordered-expected.json b/benchmarks/v1/evidence/recipe-results-042/ordered-expected.json new file mode 100644 index 0000000..12e7839 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/ordered-expected.json @@ -0,0 +1,2232 @@ +{ + "values": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + null + ] + ], + "target": [ + 0.5, + 1.0, + 2.0, + 3.0, + 5.0, + 8.0 + ], + "weight": [ + 1, + 0, + 2, + 1, + 3, + 1 + ], + "structure": [ + 0.2, + 0.56, + 0.9199999999999999, + 1.28, + 1.64, + 2.0 + ], + "records": [ + { + "family": "normal", + "mode": "natural", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.771875, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.6390625, + 0.8043281785496401 + ], + [ + 3.771875, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ], + [ + 3.9675000000000002, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8581121376974118 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.771875, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.8581121376974118 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.6390625, + 0.8581121376974118 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.6390625, + 0.774577005479161 + ], + [ + 3.771875, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.774577005479161 + ], + [ + 3.9675000000000002, + 0.8581121376974118 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.8581121376974118 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.73328125, + 0.774577005479161 + ], + [ + 4.0449375000000005, + 0.774577005479161 + ], + [ + 4.169125, + 0.8581121376974118 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.4786328125, + 0.8581121376974118 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.4786328125, + 0.774577005479161 + ], + [ + 3.73328125, + 0.774577005479161 + ], + [ + 4.0449375000000005, + 0.774577005479161 + ], + [ + 4.169125, + 0.8581121376974118 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.8953217192886782 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.73328125, + 0.7433227261868519 + ], + [ + 4.0449375000000005, + 0.7433227261868519 + ], + [ + 4.169125, + 0.8953217192886782 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.4786328125, + 0.8953217192886782 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.4786328125, + 0.7433227261868519 + ], + [ + 3.73328125, + 0.7433227261868519 + ], + [ + 4.0449375000000005, + 0.7433227261868519 + ], + [ + 4.169125, + 0.8953217192886782 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.8953217192886782 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.6966171875000002, + 0.7433227261868519 + ], + [ + 4.1165671875, + 0.7433227261868519 + ], + [ + 4.36066875, + 0.8953217192886782 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.3302353515625, + 0.8953217192886782 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.3302353515625, + 0.7433227261868519 + ], + [ + 3.6966171875000002, + 0.7433227261868519 + ], + [ + 4.1165671875, + 0.7433227261868519 + ], + [ + 4.36066875, + 0.8953217192886782 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.9210958818597373 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.3302353515625, + 0.7107475316438076 + ], + [ + 3.6966171875000002, + 0.7107475316438076 + ], + [ + 4.1165671875, + 0.7107475316438076 + ], + [ + 4.36066875, + 0.9210958818597373 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "natural", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.8125, + 0.8660951840226659 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.8660951840226659 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8660951840226659 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.792913010449562 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.7741170761258716 + ], + [ + 3.8125, + 0.8660951840226659 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.8660951840226659 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.771875, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.8660951840226659 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.6390625, + 0.8660951840226659 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.6390625, + 0.792913010449562 + ], + [ + 3.771875, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.7741170761258716 + ], + [ + 3.9675000000000002, + 0.8660951840226659 + ] + ], + "raw_after": [ + [ + 3.6390625, + 0.9097553804434051 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.771875, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.9097553804434051 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.6390625, + 0.9097553804434051 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.6390625, + 0.7636770773193933 + ], + [ + 3.771875, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.7448811429957028 + ], + [ + 3.9675000000000002, + 0.9097553804434051 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.9097553804434051 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.73328125, + 0.7448811429957028 + ], + [ + 4.0449375000000005, + 0.7448811429957028 + ], + [ + 4.169125, + 0.9097553804434051 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.4786328125, + 0.9097553804434051 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.4786328125, + 0.7636770773193933 + ], + [ + 3.73328125, + 0.7448811429957028 + ], + [ + 4.0449375000000005, + 0.7448811429957028 + ], + [ + 4.169125, + 0.9097553804434051 + ] + ], + "raw_after": [ + [ + 3.4786328125, + 0.9400424845082175 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.73328125, + 0.7140770860551798 + ], + [ + 4.0449375000000005, + 0.7140770860551798 + ], + [ + 4.169125, + 0.9400424845082175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.4786328125, + 0.9400424845082175 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.4786328125, + 0.7328730203788703 + ], + [ + 3.73328125, + 0.7140770860551798 + ], + [ + 4.0449375000000005, + 0.7140770860551798 + ], + [ + 4.169125, + 0.9400424845082175 + ] + ], + "raw_after": [ + [ + 3.3302353515625, + 0.9400424845082175 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.3302353515625, + 0.7328730203788703 + ], + [ + 3.6966171875000002, + 0.7140770860551798 + ], + [ + 4.1165671875, + 0.7140770860551798 + ], + [ + 4.36066875, + 0.9400424845082175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "ordinary", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.804368647380766, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.777785379202502, + 0.8043281785496401 + ], + [ + 3.804368647380766, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ], + [ + 3.843524237685692, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.777785379202502, + 0.924608682725661 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.804368647380766, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.924608682725661 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.777785379202502, + 0.924608682725661 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.777785379202502, + 0.7500108765756527 + ], + [ + 3.804368647380766, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.7500108765756527 + ], + [ + 3.843524237685692, + 0.924608682725661 + ] + ], + "raw_after": [ + [ + 3.7450571037169076, + 0.924608682725661 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7953948973348095, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.924608682725661 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7450571037169076, + 0.924608682725661 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7450571037169076, + 0.7500108765756527 + ], + [ + 3.7953948973348095, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.7500108765756527 + ], + [ + 3.872087861488885, + 0.924608682725661 + ] + ], + "raw_after": [ + [ + 3.7450571037169076, + 1.0025563920608493 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7953948973348095, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 1.0025563920608493 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7450571037169076, + 1.0025563920608493 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7450571037169076, + 0.7286419835078252 + ], + [ + 3.7953948973348095, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 0.6898655053380984 + ], + [ + 3.872087861488885, + 1.0025563920608493 + ] + ], + "raw_after": [ + [ + 3.7160606088874024, + 1.0025563920608493 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7663984025053043, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 1.0025563920608493 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7160606088874024, + 1.0025563920608493 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7160606088874024, + 0.7286419835078252 + ], + [ + 3.7663984025053043, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 0.6898655053380984 + ], + [ + 3.900234015609844, + 1.0025563920608493 + ] + ], + "raw_after": [ + [ + 3.7160606088874024, + 1.0577486070660242 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7160606088874024, + 0.7076929354649948 + ], + [ + 3.7663984025053043, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 0.6310834208062257 + ], + [ + 3.900234015609844, + 1.0577486070660242 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "normal", + "mode": "ordinary", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ], + [ + 3.8125, + 0.8043281785496401 + ] + ], + "raw_after": [ + [ + 3.8125, + 0.9278621894956918 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.9278621894956918 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.8125, + 0.9278621894956918 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.7814978423494837 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.743905973702103 + ], + [ + 3.8125, + 0.9278621894956918 + ] + ], + "raw_after": [ + [ + 3.7832828756784185, + 0.9278621894956918 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.9278621894956918 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7832828756784185, + 0.9278621894956918 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.7814978423494837 + ], + [ + 3.7832828756784185, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.743905973702103 + ], + [ + 3.8416864213847397, + 0.9278621894956918 + ] + ], + "raw_after": [ + [ + 3.7832828756784185, + 1.00748523648035 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 1.00748523648035 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7832828756784185, + 1.00748523648035 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.7592481343659512 + ], + [ + 3.7832828756784185, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 0.6848600582023121 + ], + [ + 3.8416864213847397, + 1.00748523648035 + ] + ], + "raw_after": [ + [ + 3.754921694450493, + 1.00748523648035 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 1.00748523648035 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.754921694450493, + 1.00748523648035 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.7592481343659512 + ], + [ + 3.754921694450493, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 0.6848600582023121 + ], + [ + 3.8704395856241702, + 1.00748523648035 + ] + ], + "raw_after": [ + [ + 3.754921694450493, + 1.0636893236179212 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 1.0636893236179212 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.754921694450493, + 1.0636893236179212 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.737554241674597 + ], + [ + 3.754921694450493, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 0.6272156665850285 + ], + [ + 3.8704395856241702, + 1.0636893236179212 + ] + ], + "raw_after": [ + [ + 3.726800811873959, + 1.0636893236179212 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.737554241674597 + ], + [ + 3.726800811873959, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 0.6272156665850285 + ], + [ + 3.8996118771377364, + 1.0636893236179212 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "formula", + "mode": "full", + "order": [ + 0, + 1 + ], + "trace": [ + { + "channel": 0, + "raw_before": [ + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.7967089830232594, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.780182181423865, + 0.5413248546129181 + ], + [ + 3.7967089830232594, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ], + [ + 3.9238996552948513, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.7967089830232594, + 0.5491305166458179 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.780182181423865, + 0.5244638662488222 + ], + [ + 3.7967089830232594, + 0.5491305166458179 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ], + [ + 3.9238996552948513, + 0.6539651725981038 + ] + ], + "raw_after": [ + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.8029753458137185, + 0.5491305166458179 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.7708608850424383, + 0.5244638662488222 + ], + [ + 3.8029753458137185, + 0.5491305166458179 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ], + [ + 4.053350348374889, + 0.6539651725981038 + ] + ], + "raw_after": [ + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.8029753458137185, + 0.5565656478147808 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.7708608850424383, + 0.5085693589334137 + ], + [ + 3.8029753458137185, + 0.5565656478147808 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ], + [ + 4.053350348374889, + 0.7537430439099552 + ] + ], + "raw_after": [ + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.8089700684903174, + 0.5565656478147808 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.76215077385381, + 0.5085693589334137 + ], + [ + 3.8089700684903174, + 0.5565656478147808 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ], + [ + 4.177880683177432, + 0.7537430439099552 + ] + ], + "raw_after": [ + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.76215077385381, + 0.49358723890248296 + ], + [ + 3.8089700684903174, + 0.563648427963561 + ], + [ + 4.177880683177432, + 0.8421092857477175 + ], + [ + 4.177880683177432, + 0.8421092857477175 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + }, + { + "family": "formula", + "mode": "full", + "order": [ + 1, + 0 + ], + "trace": [ + { + "channel": 1, + "raw_before": [ + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ], + [ + 3.7901594196053434, + 0.5413248546129181 + ] + ], + "raw_after": [ + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5492813677250686 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5242019856783096 + ], + [ + 3.7901594196053434, + 0.5492813677250686 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ], + [ + 3.7901594196053434, + 0.658507416425496 + ] + ], + "raw_after": [ + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.7965574452834443, + 0.5492813677250686 + ], + [ + 3.928823631203088, + 0.658507416425496 + ], + [ + 3.928823631203088, + 0.658507416425496 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.780708405669999, + 0.5242019856783096 + ], + [ + 3.7965574452834443, + 0.5492813677250686 + ], + [ + 3.928823631203088, + 0.658507416425496 + ], + [ + 3.928823631203088, + 0.658507416425496 + ] + ], + "raw_after": [ + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.7965574452834443, + 0.5568599250119952 + ], + [ + 3.928823631203088, + 0.761907977801221 + ], + [ + 3.928823631203088, + 0.761907977801221 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.780708405669999, + 0.5080608277211964 + ], + [ + 3.7965574452834443, + 0.5568599250119952 + ], + [ + 3.928823631203088, + 0.761907977801221 + ], + [ + 3.928823631203088, + 0.761907977801221 + ] + ], + "raw_after": [ + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.802678398839574, + 0.5568599250119952 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 1, + "raw_before": [ + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.7718776282016604, + 0.5080608277211964 + ], + [ + 3.802678398839574, + 0.5568599250119952 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ], + [ + 4.0618886954782285, + 0.761907977801221 + ] + ], + "raw_after": [ + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.802678398839574, + 0.564079096484094 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + }, + { + "channel": 0, + "raw_before": [ + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.7718776282016604, + 0.49284645485566114 + ], + [ + 3.802678398839574, + 0.564079096484094 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ], + [ + 4.0618886954782285, + 0.8531785886229585 + ] + ], + "raw_after": [ + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.7636249916684035, + 0.49284645485566114 + ], + [ + 3.8085336281300934, + 0.564079096484094 + ], + [ + 4.188902637907551, + 0.8531785886229585 + ], + [ + 4.188902637907551, + 0.8531785886229585 + ] + ], + "accepted": true, + "coefficients": [ + 0.1 + ] + } + ] + } + ] +} diff --git a/benchmarks/v1/evidence/recipe-results-042/scheduler-checks.json b/benchmarks/v1/evidence/recipe-results-042/scheduler-checks.json new file mode 100644 index 0000000..184b315 --- /dev/null +++ b/benchmarks/v1/evidence/recipe-results-042/scheduler-checks.json @@ -0,0 +1,1221 @@ +[ + { + "count": 1, + "failed_result_retained": true, + "reorder": true, + "regroup": true, + "retry": true, + "runs": { + "mixed-0": { + "state": "c18dfb158de427767f009c82051837099f40c5f6ae00a35c0807c23f7a0137c5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + } + } + }, + { + "count": 8, + "failed_result_retained": true, + "reorder": true, + "regroup": true, + "retry": true, + "runs": { + "mixed-0": { + "state": "c18dfb158de427767f009c82051837099f40c5f6ae00a35c0807c23f7a0137c5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-1": { + "state": "04c791bdea8df6eb4395fbbe334f81f2c8054b8a39803ed64c3f84cfc3886871", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-2": { + "state": "54d4dd48f53d93d8224c8d969e6b1207703d4d1ec6e487a5ec88e1ee13bae22c", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-3": { + "state": "9a1f5b9078162fec92e30e40b928cbc7493b0bc76d0892d9998bb44e2fbb1d20", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-4": { + "state": "b63dca8e05fd552d336652db6757d67aebcb98608c6da8b3466ee6c18f9a0e7a", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-5": { + "state": "7c229af07f459c011c04fa3e2ab83a6b405a0cb45061c03cdcdf45d58640f9db", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-6": { + "state": "eecaddbe0d79491fd01ee071c04f867a41651f7c4b0119f6f15518d28aadf6d5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-7": { + "state": "b14d4b28f8028e605e56293959eb76806ae88cb808e7817d31e45f36216d80bb", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + } + } + }, + { + "count": 32, + "failed_result_retained": true, + "reorder": true, + "regroup": true, + "retry": true, + "runs": { + "mixed-0": { + "state": "c18dfb158de427767f009c82051837099f40c5f6ae00a35c0807c23f7a0137c5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-1": { + "state": "04c791bdea8df6eb4395fbbe334f81f2c8054b8a39803ed64c3f84cfc3886871", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-2": { + "state": "54d4dd48f53d93d8224c8d969e6b1207703d4d1ec6e487a5ec88e1ee13bae22c", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-3": { + "state": "9a1f5b9078162fec92e30e40b928cbc7493b0bc76d0892d9998bb44e2fbb1d20", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-4": { + "state": "b63dca8e05fd552d336652db6757d67aebcb98608c6da8b3466ee6c18f9a0e7a", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-5": { + "state": "7c229af07f459c011c04fa3e2ab83a6b405a0cb45061c03cdcdf45d58640f9db", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-6": { + "state": "eecaddbe0d79491fd01ee071c04f867a41651f7c4b0119f6f15518d28aadf6d5", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-7": { + "state": "b14d4b28f8028e605e56293959eb76806ae88cb808e7817d31e45f36216d80bb", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-8": { + "state": "7c61559e6fec81ed17a3c7fc4c22df08d76e58e9dae8960e2136646eab1edfb3", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-9": { + "state": "ed9eb234d34ec2c482216bbee1abb75b7bda985a862941bc04f8939c21c9aa11", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-10": { + "state": "0c265699464af63192c22ba556ef5bc766a42817c7f28f6ab503c0ded3f84836", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-11": { + "state": "e0ca68a0c1f0d07b0d691f70a20c0c5e02e84140661cc7e2b99da6c444cb83c4", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-12": { + "state": "5facc97d3c37592d39863e120f934dcb0a1d5fad7514bcef5d891bdd3b560e2d", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-13": { + "state": "55def2a9512221b80e733ab921551f938cb00215ee6c909fe0b29e4259d5852d", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-14": { + "state": "5e70de52d0ccbaa6b9e0839bc0e83def37f9fece1d8f89e45f8c42c1233cb730", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-15": { + "state": "72439bb18f44d98d5cb4e02a5535e2156b3be45f33f6e5fee76a498ad3121b70", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-16": { + "state": "65bef2f5bc48e53fc8417957b287f0ab74fa535a3050d04b6aa749502d178119", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-17": { + "state": "34638a96c6c01fc2b17eb1df956f1b8507147cba0540f553c1f57e46145a24e8", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-18": { + "state": "0dc94ccc783ffd3d2cb726f451d7e36fcf118f908f343228c032fb11cec3c481", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-19": { + "state": "3f6385aa76fd477099d55254c73e3a00525326da6ac57caa62e56dff91194f46", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-20": { + "state": "70370578452670373c4ce2d4585f1ef6596f4510b50296240bbdf111f1cc47c8", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-21": { + "state": "ed17199529e786cc3df74e52d92c1dbad768f580b668cb341ff10740208ee9f1", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-22": { + "state": "0a6b5cc5a34ce685016e09c5e30117c01cd7016259e17f4e33cc056ad5d569ef", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-23": { + "state": "a1a5c5c45de913eb1bb43c1bd483eb7f2d84204fecd98e1d178ff011345be154", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-24": { + "state": "b3dea81203e36fa498a7f29ec62f633410e3f2c7174eafa0a119227841af3d89", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-25": { + "state": "48a0471b3fc4b2d32448b675d2f310cd8703331ecdca9ae88a0385a8778779c4", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + }, + "mixed-26": { + "state": "29efc3b2909810f259468f3fc95e97e81ff93d8d75b8e4ed8e904abf287eb07a", + "stop_round": 3, + "reason": "patience", + "version": 6, + "predictions": [ + [ + 2.81552734375, + 0.9401099036041997 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 2.81552734375, + 0.8583636934932873 + ], + [ + 3.21434375, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 0.8583636934932873 + ], + [ + 3.85762962962963, + 1.0666816835376245 + ] + ] + }, + "mixed-27": { + "state": "c3791a827ab50ab793e374dc7897ff43339a54919c3912ae86386c9f8539aba0", + "stop_round": 1, + "reason": "patience", + "version": 1, + "predictions": [ + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.09375 + ], + [ + 3.2375 + ], + [ + 3.466666666666667 + ], + [ + 3.466666666666667 + ] + ] + }, + "mixed-28": { + "state": "8782ed1e33e4451f42f1d64b721e54e263b22e898a3ae2439fcf8cf7d1902031", + "stop_round": 2, + "reason": "patience", + "version": 4, + "predictions": [ + [ + 2.94921875, + 0.944854074954142 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 2.94921875, + 0.8893944799536456 + ], + [ + 3.225625, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 0.8893944799536456 + ], + [ + 3.668888888888889, + 1.0378590498259896 + ] + ] + }, + "mixed-29": { + "state": "f228f30b9369ede3acb87821a1db4b2587045e862639f2faac887268bd10f168", + "stop_round": 3, + "reason": "patience", + "version": 3, + "predictions": [ + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 2.81552734375 + ], + [ + 3.21434375 + ], + [ + 3.85762962962963 + ], + [ + 3.85762962962963 + ] + ] + }, + "mixed-30": { + "state": "feee9e69fe161950f922095d4d4851657f9b12bfc79fb7de8e3bcc3fe17d8e5f", + "stop_round": 1, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 3.09375, + 0.9473024029133985 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.09375, + 0.9190139983940672 + ], + [ + 3.2375, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9190139983940672 + ], + [ + 3.466666666666667, + 0.9993033009019042 + ] + ] + }, + "mixed-31": { + "state": "471331f9d3008258bbf3cab9cbd5af72690f6f34786b0052ccd9c6bac2828ebb", + "stop_round": 2, + "reason": "patience", + "version": 2, + "predictions": [ + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 2.94921875 + ], + [ + 3.225625 + ], + [ + 3.668888888888889 + ], + [ + 3.668888888888889 + ] + ] + } + } + } +] diff --git a/benchmarks/v1/evidence/secondary-cpu.json b/benchmarks/v1/evidence/secondary-cpu.json new file mode 100644 index 0000000..aad8c93 --- /dev/null +++ b/benchmarks/v1/evidence/secondary-cpu.json @@ -0,0 +1,79 @@ +{ + "dirty": true, + "environment": { + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "autograd-gamma": "0.5.0", + "catboost": "1.2.10", + "cloudpickle": "3.1.2", + "contourpy": "1.3.3", + "cycler": "0.12.1", + "fonttools": "4.64.0", + "formulaic": "1.2.2", + "graphviz": "0.21", + "interface_meta": "2.0.1", + "joblib": "1.6.0", + "kiwisolver": "1.5.1", + "lifelines": "0.30.0", + "lightgbm": "4.7.0", + "matplotlib": "3.11.1", + "mpmath": "1.3.0", + "narwhals": "2.25.0", + "ngboost": "0.5.11", + "numpy": "2.3.5", + "packaging": "26.3", + "pandas": "3.0.5", + "pillow": "12.3.0", + "plotly": "7.0.0", + "pyparsing": "3.3.2", + "python-dateutil": "2.9.0.post0", + "scikit-learn": "1.8.0", + "scipy": "1.16.3", + "six": "1.17.0", + "sympy": "1.14.0", + "threadpoolctl": "3.6.0", + "tqdm": "4.70.0", + "typing_extensions": "4.16.0", + "wrapt": "2.4.0", + "xgboost": "3.4.1", + "xlrd": "2.0.2" + }, + "python": "3.12.12" + }, + "records": [ + { + "case": "NGBoost weighted Normal", + "mean_nll": 1.4375449061185919, + "status": "pass" + }, + { + "case": "Poisson rate/exposure", + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "case": "Gamma severity", + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "case": "Tweedie annualized", + "reload_max_abs_error": 0.0, + "status": "pass" + }, + { + "case": "fixed-sigma linear lognormal AFT", + "mean_nll": 0.9735669248465422, + "status": "pass" + }, + { + "case": "global saturating formula", + "max_residual": 4.351668258487951e-11, + "status": "pass" + } + ], + "scope": "tiny synthetic comparator smoke, not real quality", + "source_file_sha256": "7e769063e3f0227b29e743ac2320f5595e9c0e92c5aae0e95997eb06dad3b0e5", + "source_sha": "861d31d332c20c2d9edb5cfe39872cf32b7aff7b" +} diff --git a/benchmarks/v1/evidence/selection-cpu.json b/benchmarks/v1/evidence/selection-cpu.json new file mode 100644 index 0000000..c13200f --- /dev/null +++ b/benchmarks/v1/evidence/selection-cpu.json @@ -0,0 +1,62 @@ +{ + "scope": "synthetic execution smoke only; no E3 or performance acceptance", + "status": "pass", + "source_sha": "57301108b6987953fb557798812dd12f79f7909d", + "dirty": true, + "source_hashes": { + "selection_smoke.py": "b89f3937017c9a0ee0c474ccc7304e77369d83bdcffecdda0176db901088bcd6", + "baseline_worker.py": "d32f00f63043a4b71077017d47b4eb0e845262e318d43edbb07e4765ea338fdd", + "selection.py": "ae4bce6743379ed7a0e955347b50d3af961f400378f44256b2eeeee66c0d135f", + "process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab", + "quality.py": "6736b97666f73a0c1831fd5c9472a4fd66cc675d2b66884a684cdad92354e827" + }, + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "threads": 2, + "seed": 73, + "trials": 16, + "selected": "xgboost:15", + "protocol_sha256": "706e9c41f3c9d142d3601e3104c77ab54992973a03207ee7b431e9b249bfecd8", + "receipt_sha256": "5f92a5c2b4d7422ea0ba75010535a10f248da021df905b68faa8036ffc2d1a52", + "test_metrics": { + "rmse": 0.4359870916458243 + }, + "artifact_scope": "Synthetic smoke summary; full generated packet stays in ignored build output and is recreated by selection_smoke.py." +} diff --git a/benchmarks/v1/evidence/selection-early-stopping-cpu.json b/benchmarks/v1/evidence/selection-early-stopping-cpu.json new file mode 100644 index 0000000..d4a2bd4 --- /dev/null +++ b/benchmarks/v1/evidence/selection-early-stopping-cpu.json @@ -0,0 +1,63 @@ +{ + "scope": "synthetic execution smoke only; no E3 or performance acceptance", + "status": "pass", + "source_sha": "f9dee9c901c035052a5a7d8bde91e11921e4cda3", + "dirty": true, + "source_hashes": { + "selection_smoke.py": "0cf3b667a1b50b988d597b97dde2251ccf9fea533ba03cf4908877512baa8a75", + "baseline_worker.py": "ba20c5487cffcd67beb9f69abbcf4454b20034c1bf815d0a51b082c8e3ee7092", + "selection.py": "ae4bce6743379ed7a0e955347b50d3af961f400378f44256b2eeeee66c0d135f", + "process_runner.py": "5ccc984919d2932974f89cfc16691e79dd0e2c35a5da145993d5c9b1a0cd8eab", + "quality.py": "6736b97666f73a0c1831fd5c9472a4fd66cc675d2b66884a684cdad92354e827" + }, + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "threads": 2, + "seed": 73, + "trials": 16, + "early_stopping_rounds": 3, + "selected": "xgboost:15", + "protocol_sha256": "fcab74aaa0e76df467242a4b7bc6531546ddfc57a25208de6d7deedf83f69b14", + "receipt_sha256": "2ac300b825e48c4173df0ef6695a05f3e12fb21256e089028dca57ac0d5afb1b", + "test_metrics": { + "rmse": 0.4359870916458243 + }, + "artifact_scope": "Synthetic stopping integration summary; full raw packet is regenerated under build by selection_smoke.py." +} diff --git a/benchmarks/v1/evidence/worker-cpu.json b/benchmarks/v1/evidence/worker-cpu.json new file mode 100644 index 0000000..87a3ee3 --- /dev/null +++ b/benchmarks/v1/evidence/worker-cpu.json @@ -0,0 +1,338 @@ +{ + "scope": "synthetic numeric fixed-round baseline worker checks, not real quality", + "cells": [ + { + "library": "xgboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "exposure_doubling_max_abs_error": 1.430511474609375e-06 + }, + { + "library": "xgboost", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 8.881784197001252e-16, + "exposure_doubling_max_abs_error": 8.881784197001252e-16 + }, + { + "library": "lightgbm", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "exposure_doubling_max_abs_error": 1.7763568394002505e-15 + }, + { + "library": "catboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "ngboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + } + ], + "seed": 41, + "threads": 2, + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "source_sha": "6f144d270648e03b7bad2d66db7334bfaacbc04c", + "dirty": true, + "source_hashes": { + "worker_smoke.py": "7f17ad465fae4cc15bc701ba492545cfb4c7880073270ec60c9bb55f9ed65c00", + "baseline_worker.py": "d32f00f63043a4b71077017d47b4eb0e845262e318d43edbb07e4765ea338fdd" + } +} diff --git a/benchmarks/v1/evidence/worker-target-scale-cpu.json b/benchmarks/v1/evidence/worker-target-scale-cpu.json new file mode 100644 index 0000000..ebf90dc --- /dev/null +++ b/benchmarks/v1/evidence/worker-target-scale-cpu.json @@ -0,0 +1,1878 @@ +{ + "scope": "Synthetic baseline target scaling and replay regression; not real quality or CUDA evidence", + "seed": 41, + "threads": 2, + "python": "3.12.12", + "os": "macOS-26.3-x86_64-i386-64bit", + "source_sha": "f43ae75e048ca80ceaf5b6a3057abff5a837d6a2", + "dirty": true, + "source_hashes": { + "baseline_worker.py": "23a77399fe0d8f22ca8562801e43ea50e6ca492501c2feae278937e8c44b69b0", + "worker_smoke.py": "50aeba765e4b9348983d19a74c05b22d21c21e738e821674ef81109e02a018b6", + "preprocessing.py": "62561effccd6856bc404339dddbc8c2daa3a1d0b011be59edd8fd3435802bc30" + }, + "packages": { + "autograd": "1.9.1", + "threadpoolctl": "3.6.0", + "pandas": "3.0.5", + "cloudpickle": "3.1.2", + "kiwisolver": "1.5.1", + "packaging": "26.3", + "joblib": "1.6.0", + "lifelines": "0.30.0", + "contourpy": "1.3.3", + "ngboost": "0.5.11", + "autograd-gamma": "0.5.0", + "numpy": "2.3.5", + "matplotlib": "3.11.1", + "formulaic": "1.2.2", + "sympy": "1.14.0", + "xlrd": "2.0.2", + "narwhals": "2.25.0", + "plotly": "7.0.0", + "wrapt": "2.4.0", + "python-dateutil": "2.9.0.post0", + "mpmath": "1.3.0", + "scipy": "1.16.3", + "fonttools": "4.64.0", + "cycler": "0.12.1", + "pyparsing": "3.3.2", + "lightgbm": "4.7.0", + "graphviz": "0.21", + "tqdm": "4.70.0", + "interface_meta": "2.0.1", + "typing_extensions": "4.16.0", + "pillow": "12.3.0", + "catboost": "1.2.10", + "six": "1.17.0", + "scikit-learn": "1.8.0", + "xgboost": "3.4.1" + }, + "fixed": [ + { + "library": "xgboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "target_scale": { + "mean": [ + 97.33608092971467, + -299.99916918973963, + 7.0 + ], + "std": [ + 20.982425267340897, + 0.009358851635228047, + 1.0 + ], + "constant": [ + false, + false, + true + ] + } + }, + { + "library": "xgboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "exposure_doubling_max_abs_error": 1.430511474609375e-06 + }, + { + "library": "xgboost", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "target_scale": { + "mean": [ + 97.12298174226333, + -299.99916918973963, + 7.0 + ], + "std": [ + 21.146159638860134, + 0.009358851635228047, + 1.0 + ], + "constant": [ + false, + false, + true + ] + } + }, + { + "library": "lightgbm", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 8.881784197001252e-16, + "exposure_doubling_max_abs_error": 8.881784197001252e-16 + }, + { + "library": "lightgbm", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "target_scale": { + "mean": [ + 97.87910794732994, + -299.99916918973963, + 7.0 + ], + "std": [ + 20.728930204245078, + 0.009358851635228047, + 1.0 + ], + "constant": [ + false, + false, + true + ] + } + }, + { + "library": "catboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "exposure_doubling_max_abs_error": 1.7763568394002505e-15 + }, + { + "library": "catboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0 + }, + { + "library": "ngboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0 + } + ], + "stopping": [ + { + "library": "xgboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "rmse": [ + 1.3190258050330348, + 1.4036078889311892, + 1.4715428645880309, + 1.5328491276069665 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "logloss": [ + 0.6347751041253408, + 0.5559325362245242, + 0.4902409628033638, + 0.43469255169232685, + 0.3871888145804405, + 0.34620265463987987, + 0.3105856830875079, + 0.2794518657028675, + 0.252102733651797, + 0.2279777045051257, + 0.2066204644739628, + 0.18765461444854736, + 0.17076645642518998, + 0.1556919942299525, + 0.1422073448697726, + 0.13012115868429344, + 0.11926914242406686, + 0.10950897460182508, + 0.10071733991305033, + 0.09278654331962267, + 0.08562237073977788, + 0.07914204808572928, + 0.07327268011868, + 0.06794991387675206 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "mlogloss": [ + 1.100152987241745, + 1.1028474867343903, + 1.1085578600565593, + 1.1105650146802266 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.2858650093484256, + 0.27033074512663813, + 0.2566173301747767, + 0.2440655096123616, + 0.23339290898293258, + 0.2228521027447035, + 0.21307654707278642, + 0.20405924214153653, + 0.19786285429468586, + 0.190334304013393, + 0.184337596114104, + 0.17870675332637298, + 0.1734560108329687, + 0.16988741957789494, + 0.16622945896556807, + 0.16233782992801732, + 0.15888993436636964, + 0.1556452370688526, + 0.15289295974022632, + 0.1503074509015682, + 0.14764388453267102, + 0.1446768974678384, + 0.14224238008157247, + 0.13988932841457427 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "target_scale": { + "mean": [ + 97.33608092971467, + -299.99916918973963, + 7.0 + ], + "std": [ + 20.982425267340897, + 0.009358851635228047, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "rmse": [ + 0.8429763766078199, + 0.799077047324877, + 0.751266980294627, + 0.7131527286284766, + 0.6700382161418184, + 0.6385647688348568, + 0.6031783517146202, + 0.5723583162326304, + 0.543924393710344, + 0.5217226241075557, + 0.5001475288120608, + 0.4811105176885543, + 0.4606970563184126, + 0.4419260053189856, + 0.42272875145377126, + 0.4063129943683274, + 0.3907909581352647, + 0.378917473962742, + 0.36115365850205766, + 0.34875944603432346, + 0.3350962613280355, + 0.3250604371721964, + 0.31484387565008004, + 0.3053118664171941 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "xgboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "poisson-nloglik": [ + 2.20390141805013, + 2.137744075059891, + 2.0731852829456328, + 2.0092012087504068, + 1.9474283715089162, + 1.8912934521834055, + 1.8333994368712108, + 1.777717645963033, + 1.7242320875326793, + 1.6736395716667176, + 1.6256426950295766, + 1.5802442669868468, + 1.5382574121157329, + 1.497567985455195, + 1.459828053911527, + 1.4223221321900685, + 1.38948645790418, + 1.3557770192623138, + 1.3250512798627219, + 1.2974480837583542, + 1.2731199165185292, + 1.2558044453461965, + 1.2382590234279633, + 1.224551365772883 + ] + } + } + } + ], + "exposure_doubling_max_abs_error": 2.288818359375e-05 + }, + { + "library": "xgboost", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "gamma-deviance": [ + 1.0479340935785635, + 0.8765117834399765, + 0.7362795183202252, + 0.6253385437477845, + 0.5339218441857763, + 0.4751267791473462, + 0.4329137079960977, + 0.39694250947989834, + 0.3668045233808546, + 0.340728317486014, + 0.31514107499412297, + 0.2947485263991742, + 0.2759979225789607, + 0.25946758926849234, + 0.2446441778893738, + 0.22804094806667383, + 0.2168541413966892, + 0.20469011664560335, + 0.19624207265745403, + 0.1870708379727148, + 0.17879051575085517, + 0.161901100057518, + 0.14734316934094144, + 0.1421370795503511 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "tweedie-nloglik@1.5": [ + 5.518956836064657, + 5.400989929835002, + 5.338087542851766, + 5.288453420003255, + 5.2157480955123905, + 5.173163398106893, + 5.1388852516810095, + 5.105915009975433, + 5.078025571505228, + 5.053517389297485, + 5.031838723023733, + 5.0116044044494625, + 4.996154860655467, + 4.9798126777013145, + 4.965713342030843, + 4.954478104909261, + 4.943256398042043, + 4.921875250339508, + 4.914606960614522, + 4.899503707885742, + 4.893172804514567, + 4.887858029206594, + 4.88621164560318, + 4.88164013226827 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "aft-nloglik": [ + 1.7734065099076717, + 1.6197563711867196, + 1.4976458711955158, + 1.3925457275547937, + 1.3060255839984132, + 1.2423128446853335, + 1.1810616951872548, + 1.1322734221484718, + 1.0842894627997242, + 1.051445002718269, + 1.0178983639780095, + 0.996230571710108, + 0.974674918964329, + 0.9576461062084315, + 0.947507673646346, + 0.9303152329498858, + 0.921520709098253, + 0.9122261276779752, + 0.9023172283725974, + 0.8986914384873738, + 0.8955653694450139, + 0.8963714056732957, + 0.8929962766000903, + 0.891572775275496 + ] + } + } + } + ] + }, + { + "library": "xgboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "rmse": [ + 1.2325364259370402, + 1.3162893835804081, + 1.3915442471106123, + 1.4624127026649032 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "l2": [ + 1.4339700264041833, + 1.6320412485288422, + 1.8262533605839342, + 2.014177389235853 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "binary_logloss": [ + 0.6213523371820071, + 0.5486967349282297, + 0.4915110010453842, + 0.43862033990901683, + 0.3964246722120901, + 0.3564900332451924, + 0.3245027618622304, + 0.29359040565025213, + 0.2688830086303527, + 0.2445157370328498, + 0.22516882732980115, + 0.20569010040309055, + 0.19038254133236984, + 0.17463491065912654, + 0.162423553254113, + 0.1495711180099389, + 0.13976376723694678, + 0.1291873115752735, + 0.12126508879262181, + 0.11237800372659577, + 0.10595621900413515, + 0.09821247197895472, + 0.09120678856312132, + 0.08644717145310317 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "multi_logloss": [ + 1.1023683272199676, + 1.1083206450932424, + 1.1135855303108044, + 1.1227674358376611 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.20262679884781276, + 0.18336815029756953, + 0.16603536660235063, + 0.15043586127665368, + 0.14190466230571763, + 0.13530027920953328, + 0.1326058509954632, + 0.1301808656028002, + 0.1250262083326152, + 0.12332935379671887, + 0.12171725754813778, + 0.12225176419036404, + 0.12273282016836767, + 0.12088695174835097, + 0.11905326646563896, + 0.11740294971119818, + 0.11479637405602891, + 0.11245045596637657, + 0.11033912968568946, + 0.10843893603307106, + 0.10672876174571448, + 0.10518960488709357, + 0.10380436371433475, + 0.10255764665885182 + ] + } + } + }, + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.46272389098477074, + 0.43479768981829464, + 0.3986126022112219, + 0.36609509986929833, + 0.33930495582199666, + 0.3144555217749054, + 0.29209103113252316, + 0.27190816449930755, + 0.2544210185526883, + 0.24448489405418553, + 0.2362546523105289, + 0.22958138563753963, + 0.22357544563184942, + 0.21817009962672806, + 0.21551390001197354, + 0.2108689231161251, + 0.20851239626691334, + 0.20534328627993131, + 0.20334297432610757, + 0.201758284996264, + 0.20040193099322848, + 0.19910710838821022, + 0.19794176804369384, + 0.1968929617336291 + ] + } + } + }, + { + "selected_rounds": 24, + "history": { + "validation": { + "quantile": [ + 0.2004767091707326, + 0.19540910000011733, + 0.19084825174656356, + 0.18674348831836524, + 0.18304920123298668, + 0.17972434285614594, + 0.17673197031698937, + 0.1740388350317484, + 0.17161501327503154, + 0.1694335736939864, + 0.16747027807104573, + 0.16570331201039915, + 0.16411304255581718, + 0.16268180004669344, + 0.1613936817884821, + 0.16023437535609186, + 0.15919099956694066, + 0.15825196135670458, + 0.15741825212500785, + 0.15573088588850859, + 0.15421225627565932, + 0.15236331115894294, + 0.15069926055389823, + 0.14920161500935797 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "target_scale": { + "mean": [ + 97.12298174226333, + -299.99916918973963, + 7.0 + ], + "std": [ + 21.146159638860134, + 0.009358851635228047, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "l2": [ + 1.0045199327503227, + 0.8656416269365679, + 0.7530379416083925, + 0.6682374561277321, + 0.5929966625658722, + 0.532159895051236, + 0.4963075217686542, + 0.4550182748621944, + 0.4206349258876807, + 0.3945914344087369, + 0.373764595064519, + 0.3552464775805187, + 0.3414635215747352, + 0.3305251923111525, + 0.32187806406151714, + 0.3142013033012881, + 0.30783989394884076, + 0.3035735595415485, + 0.30187319740278784, + 0.2992481446541362, + 0.2973773333106822, + 0.2957570860665345, + 0.29566556265617044, + 0.29476501916799863 + ] + } + } + }, + { + "selected_rounds": 24, + "history": { + "validation": { + "l2": [ + 1.0611874164531143, + 0.8951731258211216, + 0.7861684508190062, + 0.6578204650379862, + 0.5560395164427997, + 0.46882414619185503, + 0.3995936917395681, + 0.34203063325660943, + 0.29543224169692067, + 0.2596515665269301, + 0.22885359928945165, + 0.20578795278416961, + 0.191540552760643, + 0.1738696990477073, + 0.15967113978561426, + 0.14875300426920074, + 0.13994204767865123, + 0.1328408597362526, + 0.12682791221968298, + 0.12206241651500879, + 0.11830159154211364, + 0.11620344297968842, + 0.11369955573174817, + 0.11175558319192934 + ] + } + } + }, + { + "selected_rounds": 1, + "history": { + "validation": { + "l2": [ + 0.0, + 0.0, + 0.0, + 0.0 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "lightgbm", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 2.220446049250313e-16, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "poisson": [ + 0.5005120682856539, + 0.422793601689382, + 0.34580249019092385, + 0.290426369063737, + 0.24331593445585753, + 0.20297940161932224, + 0.1479531677784868, + 0.09972843745070623, + 0.05763817628021955, + 0.020701224085593377, + -0.011864898761765937, + -0.057322727023509844, + -0.0982475089257662, + -0.13525515173003388, + -0.16884838830065016, + -0.199443210906951, + -0.22738795117013721, + -0.2529773198588645, + -0.27646294758689766, + -0.29806142942063235, + -0.31672629630194105, + -0.34545249292066094, + -0.3713268060116866, + -0.394710227632587 + ] + } + } + } + ], + "exposure_doubling_max_abs_error": 1.7763568394002505e-15 + }, + { + "library": "lightgbm", + "application": "A8", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "validation": { + "gamma": [ + 1.5572463427516845, + 1.4790847906375282, + 1.4208560534542, + 1.3718738435738709, + 1.3284338373910762, + 1.291559985335919, + 1.2597323091754626, + 1.2299014398363803, + 1.2046453972127822, + 1.1841482321865366, + 1.1665933324277225, + 1.152620605189197, + 1.1406934096617745, + 1.1311367356689008, + 1.122463128523127, + 1.115715668854146, + 1.1095942007094153, + 1.1049684229027839, + 1.1006553581113887, + 1.0975158065819997, + 1.0954517411852744, + 1.0936720854384978, + 1.0919630295314093, + 1.0904683663383878 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 22, + "history": { + "validation": { + "tweedie": [ + 5.607787186534998, + 5.509538807869294, + 5.428697572677188, + 5.34550429435114, + 5.273467998541048, + 5.212112972295268, + 5.159684193358059, + 5.115821953277433, + 5.086649656958025, + 5.053047124181921, + 5.023873475858671, + 5.001380388206773, + 4.979656341299342, + 4.960872260315704, + 4.949660973306719, + 4.935165462008523, + 4.922809967014516, + 4.915788033975601, + 4.906080205374078, + 4.909411616047705, + 4.905959803551567, + 4.897560788539666, + 4.901267775115338, + 4.898782329247837 + ] + } + } + } + ] + }, + { + "library": "lightgbm", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "validation": { + "l2": [ + 1.51742705964158, + 1.7145253260664564, + 1.9013897600772813, + 2.0881430812946595 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A1", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "learn": { + "RMSE": [ + 1.012978198206559, + 0.9638517146626504, + 0.918770729350706, + 0.8812063908365043 + ] + }, + "validation": { + "RMSE": [ + 1.2565735750613158, + 1.3151692365662933, + 1.3648672921346283, + 1.3899750915034053 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A2", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "Logloss": [ + 0.6219704945882162, + 0.561628892686632, + 0.5083741929796007, + 0.4696956634521484, + 0.4294425964355469, + 0.39045291476779515, + 0.36120088365342884, + 0.333604007297092, + 0.30612419976128474, + 0.2881415049235026, + 0.2686185201009115, + 0.2501450856526693, + 0.2337204827202691, + 0.21997019449869792, + 0.20522193908691405, + 0.19628673129611546, + 0.18597842322455513, + 0.17601354387071397, + 0.16649158265855576, + 0.1606853167215983, + 0.1525210910373264, + 0.14609443876478406, + 0.14049678378634983, + 0.1334196514553494 + ] + }, + "validation": { + "Logloss": [ + 0.6110025365276788, + 0.548011164566562, + 0.4929916362186898, + 0.4519312493425164, + 0.4097429817307997, + 0.3702728367482893, + 0.34000206682735373, + 0.3124483559643589, + 0.28322176823640866, + 0.2606445644828456, + 0.24009714646247338, + 0.2217773070684931, + 0.20508254736425566, + 0.18985914597466846, + 0.17475723343897934, + 0.16377955391405397, + 0.1521867496431274, + 0.14213228058945201, + 0.13249683015636232, + 0.12463454520637564, + 0.11629010878308264, + 0.10949412901085756, + 0.10414688259153193, + 0.097059226994506 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A3", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 3, + "history": { + "learn": { + "MultiClass": [ + 1.0929380335149894, + 1.0894810901716065, + 1.084618211478942, + 1.0808856236425897, + 1.0662837720490004, + 1.0599046776994747 + ] + }, + "validation": { + "MultiClass": [ + 1.094600839854673, + 1.0939455114552343, + 1.0910410345850077, + 1.0915626854403928, + 1.1006849637538019, + 1.1061757178910294 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A5", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "MultiQuantile:alpha=0.1,0.5,0.9": [ + 0.23795171446057345, + 0.22486282054026685, + 0.20700150121524433, + 0.1969311916477969, + 0.18502166462821842, + 0.17578967316917882, + 0.16694645064868566, + 0.16062172308677966, + 0.1553732939565879, + 0.14866641698088479, + 0.14346283452404374, + 0.13768243393514387, + 0.13151083874630615, + 0.12687066929868346, + 0.12141076967964302, + 0.11742501508776022, + 0.11351216678447733, + 0.10863012674418217, + 0.10468313310966468, + 0.10143105766651851, + 0.09853063251110565, + 0.09474771915460732, + 0.09355363534804637, + 0.09032813021602075 + ] + }, + "validation": { + "MultiQuantile:alpha=0.1,0.5,0.9": [ + 0.2683737281615697, + 0.25505476270747607, + 0.24047204213131337, + 0.22871319164658474, + 0.21574672513842347, + 0.20143773927084455, + 0.19013389513333112, + 0.1829532674981523, + 0.17675633271104613, + 0.16998474623192375, + 0.1623963856542065, + 0.15641688839207354, + 0.14924082173419387, + 0.14420181216398478, + 0.13748433549612987, + 0.1330885746444381, + 0.128560208839024, + 0.1211921860558859, + 0.11654170600480954, + 0.11329442192260186, + 0.11190234244550304, + 0.10581998514947175, + 0.10553367005359486, + 0.10273571400080492 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A6", + "status": "pass", + "shape": [ + 24, + 3 + ], + "reload_max_abs_error": 0.0, + "target_scale": { + "mean": [ + 97.87910794732994, + -299.99916918973963, + 7.0 + ], + "std": [ + 20.728930204245078, + 0.009358851635228047, + 1.0 + ], + "constant": [ + false, + false, + true + ] + }, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "MultiRMSE": [ + 1.3669438025002956, + 1.331257545087125, + 1.2819642327262948, + 1.253838531655201, + 1.2186120746041114, + 1.1843453359244653, + 1.123697784765684, + 1.0774398181839804, + 1.0261400922598767, + 0.9977762261094046, + 0.9793007615486897, + 0.9459309676359444, + 0.9035509403523742, + 0.8967337098462821, + 0.8767324877466302, + 0.855007436812717, + 0.8269576005029343, + 0.79477205526783, + 0.7669437802775406, + 0.765615440800123, + 0.7334802605096512, + 0.7033515595716432, + 0.6863243484265152, + 0.6624757272253102 + ] + }, + "validation": { + "MultiRMSE": [ + 1.5266038470562657, + 1.4909324057076387, + 1.438909839780333, + 1.395946638714238, + 1.3497062001385742, + 1.308832883083957, + 1.2363764661549066, + 1.182874710900528, + 1.127801458040031, + 1.1028503566184642, + 1.0777243757474846, + 1.039304427903847, + 0.9917635965002015, + 0.9842715316128643, + 0.9670499473711812, + 0.9461422331463335, + 0.91447533859761, + 0.8797466256881813, + 0.846955529082318, + 0.8473319909313768, + 0.8130513351647627, + 0.7858032303382598, + 0.7691159356436589, + 0.7435669072217916 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + }, + { + "library": "catboost", + "application": "A7", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "learn": { + "Poisson": [ + 0.34115288266295785, + 0.17163258586769625, + 0.06935736559140565, + 0.006976786388112638 + ] + }, + "validation": { + "Poisson": [ + 0.5436233691285653, + 0.5441104842227786, + 0.5455787764793082, + 0.5476343151481607 + ] + } + } + } + ], + "exposure_doubling_max_abs_error": 8.881784197001252e-16 + }, + { + "library": "catboost", + "application": "A9", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 24, + "history": { + "learn": { + "Tweedie:variance_power=1.5": [ + 5.395901885940239, + 5.31438091292526, + 5.239908625260069, + 5.1746406281609625, + 5.098447000871545, + 5.036321793511357, + 4.991825238806246, + 4.952675599797405, + 4.877352841234589, + 4.844477487052761, + 4.818065467823365, + 4.774566971038388, + 4.752227005776371, + 4.706654125604416, + 4.672446385175128, + 4.636186462188453, + 4.620028683028504, + 4.5905200903852235, + 4.565784184556538, + 4.557785490821003, + 4.538361179212804, + 4.528709929584562, + 4.508661048137493, + 4.4909689402950335 + ] + }, + "validation": { + "Tweedie:variance_power=1.5": [ + 5.745467801742013, + 5.726303797360546, + 5.709370680140718, + 5.695808670766458, + 5.606172101819614, + 5.57325887811127, + 5.564462717112858, + 5.557517215511638, + 5.445828590546447, + 5.378909156432513, + 5.375111649815649, + 5.328188702665831, + 5.27958032934185, + 5.214390941693139, + 5.183009137942778, + 5.147350032871332, + 5.133999953244005, + 5.091861744494804, + 5.063682960409003, + 5.051001374020511, + 5.024043987819706, + 5.0075000446987055, + 4.980631458052991, + 4.958488095496281 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A10", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 15, + "history": { + "learn": { + "SurvivalAft:dist=Normal;scale=1": [ + 1.219789320155794, + 1.1706216598129104, + 1.1332653679855706, + 1.0841168178607268, + 1.0472706050620793, + 1.0009581504591332, + 0.9695527762048747, + 0.9431649422721458, + 0.9135642680224603, + 0.9062331585689414, + 0.8867602462247459, + 0.847795635730943, + 0.8377684694081913, + 0.8348016509081654, + 0.7927069627728359, + 0.7898133068296669, + 0.7653165971901826, + 0.7523044359822811 + ] + }, + "validation": { + "SurvivalAft:dist=Normal;scale=1": [ + 1.3955753705667415, + 1.321899973100922, + 1.2550588127495719, + 1.175831253626942, + 1.0981945525326349, + 1.054609839279401, + 1.0294327994651467, + 0.9906220246460042, + 0.9560634013735984, + 0.9563624908333638, + 0.9248062050366707, + 0.8911395684636398, + 0.8826435814936511, + 0.894168569369036, + 0.8714135782826808, + 0.8754711344066806, + 0.8915317046794049, + 0.8953527579569182 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 23, + "history": { + "learn": { + "RMSEWithUncertainty": [ + 1.4437337880647023, + 1.388591175136111, + 1.3394736509750325, + 1.3148899503399802, + 1.2525780433181535, + 1.1296700568623643, + 1.0583881347139599, + 1.0031424494938346, + 0.9333272805319993, + 0.8994600004249895, + 0.8447431047893572, + 0.7790904947401334, + 0.7396515125701246, + 0.7182874646381202, + 0.6552904287415786, + 0.6313427199690579, + 0.6180207983132339, + 0.5424822981927696, + 0.5135235276295191, + 0.4807097496726043, + 0.45048730775215434, + 0.39041639157722957, + 0.37291975803678706, + 0.3619668531463122 + ] + }, + "validation": { + "RMSEWithUncertainty": [ + 1.541215145829388, + 1.4527687196646966, + 1.4125794877324218, + 1.3834051632580109, + 1.3111479061082207, + 1.216929760923946, + 1.166293660558661, + 1.1064114362896835, + 1.035247449425124, + 1.0197575864610606, + 0.9619242000356178, + 0.9146876644295338, + 0.88904786602617, + 0.887673090554584, + 0.8081619845834918, + 0.8079486890282249, + 0.8126852591446043, + 0.7355369003249014, + 0.7126861751971094, + 0.6966942887634755, + 0.6893168889951754, + 0.6344391606556814, + 0.6312624987718344, + 0.6365117046715719 + ] + } + } + } + ] + }, + { + "library": "catboost", + "application": "A12", + "status": "pass", + "shape": [ + 24 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "learn": { + "RMSE": [ + 1.019097744399966, + 0.9720372824809729, + 0.9281210336558733, + 0.8900834723731033 + ] + }, + "validation": { + "RMSE": [ + 1.2260142676069548, + 1.2848601534469415, + 1.335108608040352, + 1.3590492444945887 + ] + } + } + } + ] + }, + { + "library": "ngboost", + "application": "A11", + "status": "pass", + "shape": [ + 24, + 2 + ], + "reload_max_abs_error": 0.0, + "stopping": [ + { + "selected_rounds": 1, + "history": { + "train": { + "LOGSCORE": [ + 1.4711392929040266, + 1.3111732440489416, + 1.2245807802179178, + 1.089387298688345 + ] + }, + "val": { + "LOGSCORE": [ + 1.6596083297932713, + 1.6718481027878496, + 1.747947079640107, + 1.871604884969043 + ] + } + } + } + ], + "new_process_max_abs_error": 0.0 + } + ] +} diff --git a/benchmarks/v1/freeze_preprocessing.py b/benchmarks/v1/freeze_preprocessing.py new file mode 100644 index 0000000..36527e3 --- /dev/null +++ b/benchmarks/v1/freeze_preprocessing.py @@ -0,0 +1,155 @@ +"""Freeze train-only numeric encoding and support metadata for available real tasks.""" + +import argparse +import hashlib +import json +import platform +import subprocess +import sys +from pathlib import Path + +import numpy as np + +from benchmarks.v1 import adult, bike, housing, real_data +from benchmarks.v1.preprocessing import censoring_support, fit_encoder, fit_target_scale, transform + + +def prepare(name, data, folds, categories=None): + categories = categories or {} + records = [] + for seed, parts in enumerate(folds): + train = parts["train"] + enc = fit_encoder(data["x"][train], {k: v[train] for k, v in categories.items()}) + record = {"seed": seed, "encoder": enc, "partitions": {}} + if name == "parkinsons": + record["target_scale"] = fit_target_scale(data["y"][train]) + if name == "veteran": + record["censoring_support"] = censoring_support(data["y"][train], data["event"][train]) + for part, rows in parts.items(): + result = transform(enc, data["x"][rows], {k: v[rows] for k, v in categories.items()}) + record["partitions"][part] = { + "shape": list(result.shape), + "x_sha256": real_data.array_hash(result), + "rows_sha256": real_data.array_hash(rows), + } + records.append(record) + return records + + +def describe(root, adult_path, bike_path, housing_path): + result = {} + for name in ["covertype", "parkinsons", "concrete", "veteran", "insurance"]: + if name == "insurance": + data, _ = real_data.insurance(root) + rule = "group" + else: + data, rule = real_data.load(root, name) + categories = {k[9:]: v for k, v in data.items() if k.startswith("category_")} + if name == "veteran": + # Restore source-declared categories before one-hot encoding; no ordinal assumption. + for col, key, labels in [ + (0, "Treatment", ["standard", "test"]), + (1, "Celltype", ["adeno", "large", "smallcell", "squamous"]), + (5, "Prior_therapy", ["no", "yes"]), + ]: + categories[key] = np.asarray(labels)[data["x"][:, col].astype(int)] + data["x"] = data["x"][:, 2:5] + folds = [] + for seed in range(5): + split = ( + real_data.group_splits(data["group"], seed) + if rule == "group" + else real_data.stratified_splits( + data["event"] if rule == "event" else data["y"], seed + ) + ) + folds.append(dict(zip(["train", "validation", "test"], split, strict=True))) + result[name] = prepare(name, data, folds, categories) + if name == "insurance": + for suffix, original_rows in [ + ("severity", data["severity_policy_row"]), + ("aggregate", np.flatnonzero(data["aggregate_eligible"])), + ]: + filtered = {"x": data["x"][original_rows]} + selected_folds = [ + { + part: np.flatnonzero(np.isin(original_rows, rows)) + for part, rows in fold.items() + } + for fold in folds + ] + selected_categories = {k: v[original_rows] for k, v in categories.items()} + result["insurance_" + suffix] = prepare( + suffix, filtered, selected_folds, selected_categories + ) + + first, last = adult.load_archive(adult_path) + n = len(first["y"]) + raw = np.asarray(first["x"] + last["x"], dtype=object) + numeric = [i for i, c in enumerate(adult.FEATURES) if c in adult.NUMERIC] + cats = {c: raw[:, i] for i, c in enumerate(adult.FEATURES) if c not in adult.NUMERIC} + data = {"x": raw[:, numeric].astype(float)} + folds = [] + for seed in range(5): + train, val = adult.stratified_split(first["y"], seed) + folds.append( + {"train": train, "validation": val, "test": np.arange(n, len(raw), dtype="= 5") + if type(seed) is not int or seed < 0: + raise ValueError("seed must be a nonnegative integer") + order = np.random.default_rng(seed).permutation(n_samples).astype("= 0, "invalid seed") + _require(isinstance(cell["config"], dict), "config must be an object") + _require(applications == APPLICATIONS, "each A1-A13 needs a required CPU cell") + _canonical(manifest) # disallow non-finite nested config/provenance values + return {cell["id"]: cell for cell in cells} + + +def _pairs(pairs): + result = {} + for key, value in pairs: + _require(key not in result, "duplicate JSON field") + result[key] = value + return result + + +def read_json(data): + value = json.loads(data, object_pairs_hook=_pairs) + _canonical(value) # also rejects JSON NaN/Infinity and overflowing numeric literals + return value + + +def _artifact(root, entry): + _object(entry, "path sha256") + _text(entry["path"]) + _hash(entry["sha256"]) + relative = Path(entry["path"]) + _require(not relative.is_absolute() and ".." not in relative.parts, "unsafe artifact path") + path = (root / relative).resolve() + _require(path.is_relative_to(root), "artifact escapes run directory") + data = path.read_bytes() + _require(hashlib.sha256(data).hexdigest() == entry["sha256"], "artifact hash mismatch") + return data + + +def _predictions(data): + values = read_json(data) + _require(isinstance(values, list) and values, "predictions must be a nonempty JSON array") + + def shape(value): + if isinstance(value, list): + _require(bool(value), "empty prediction dimension") + shapes = [shape(v) for v in value] + _require(all(s == shapes[0] for s in shapes), "ragged predictions") + return (len(value), *shapes[0]) + _require(type(value) in (int, float) and math.isfinite(value), "invalid prediction value") + return () + + shape(values) + + +def _case(record, cell, manifest, root): + _object(record, "id status cache_key backend fallback exit_code artifacts metrics reason") + _require(record["status"] in STATUSES, "unknown status") + _require(record["cache_key"] == cache_key(manifest, cell), "cache identity mismatch") + _require(record["backend"] in {"cpu", "cuda", "none"}, "unknown actual backend") + _require(type(record["fallback"]) is bool, "fallback must be boolean") + _require(type(record["exit_code"]) is int or record["exit_code"] is None, "invalid exit code") + _require(isinstance(record["reason"], str), "invalid reason") + _require(isinstance(record["metrics"], dict), "invalid metrics") + for name, value in record["metrics"].items(): + _text(name) + _require(type(value) in (int, float) and math.isfinite(value), "non-finite/invalid metric") + artifacts = record["artifacts"] + _require(isinstance(artifacts, dict), "invalid artifacts") + _require( + set(artifacts) <= {"predictions", "model", "log"} and "log" in artifacts, + "missing log or unknown artifact role", + ) + payloads = {role: _artifact(root, entry) for role, entry in artifacts.items()} + if "predictions" in payloads: + _predictions(payloads["predictions"]) + if record["status"] == "pass": + _require(record["exit_code"] == 0, "worker did not exit successfully") + _require( + record["backend"] == cell["backend"] and not record["fallback"], + "backend mismatch or fallback", + ) + _require(set(artifacts) == {"predictions", "model", "log"}, "missing raw predictions/model") + _require(bool(record["metrics"]), "missing metrics") + else: + _text(record["reason"]) + _require(not cell["required"], f"required status is {record['status']}") + + +def judge(manifest, records, directory): + """Check all declared cells, retaining errors. Integrity is necessary, not sufficient.""" + report = { + "schema": SCHEMA, + "integrity_pass": False, + "errors": [], + "statuses": {}, + "gate_results": {}, + "scope": "artifact integrity only; quality and E0-E7 not evaluated", + } + errors = report["errors"] + try: + expected = _manifest(manifest) + _require(isinstance(records, list), "cases must be a list") + root = Path(directory).resolve() + seen = set() + for record in records: + try: + _require(isinstance(record, dict), "case must be an object") + case_id = record.get("id") + _text(case_id) + _require(case_id in expected, f"unknown case: {case_id}") + _require(case_id not in seen, f"duplicate case: {case_id}") + seen.add(case_id) + report["statuses"][case_id] = record.get("status") + _case(record, expected[case_id], manifest, root) + except (ValueError, TypeError, OSError, OverflowError) as exc: + errors.append( + f"case {record.get('id') if isinstance(record, dict) else '?'}: {exc}" + ) + errors.extend(f"missing case: {case_id}" for case_id in sorted(expected.keys() - seen)) + except (ValueError, TypeError, OSError, OverflowError) as exc: + errors.append(f"manifest: {exc}") + report["integrity_pass"] = not errors + return report + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + args = parser.parse_args() + try: + manifest = read_json((args.directory / "manifest.json").read_bytes()) + records = [ + read_json(line) for line in (args.directory / "cases.jsonl").read_bytes().splitlines() + ] + report = judge(manifest, records, args.directory) + except (ValueError, TypeError, OSError, OverflowError) as exc: + report = { + "schema": SCHEMA, + "integrity_pass": False, + "errors": [str(exc)], + "gate_results": {}, + } + print(json.dumps(report, sort_keys=True, allow_nan=False)) + return 0 if report["integrity_pass"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/v1/modal_preflight.py b/benchmarks/v1/modal_preflight.py new file mode 100644 index 0000000..17122b9 --- /dev/null +++ b/benchmarks/v1/modal_preflight.py @@ -0,0 +1,214 @@ +"""Allowlisted comparator preflight; uploads no production source or datasets.""" + +import hashlib +import json +import subprocess +from pathlib import Path + +import modal + +ROOT = Path(__file__).resolve().parents[2] +SOURCE = Path(__file__).with_name("capability_smoke.py") +LOCK = Path(__file__).with_name("requirements-cuda.txt") +SOURCE_SHA = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip() +DIRTY = bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=ROOT)) +HASH = hashlib.sha256(SOURCE.read_bytes()).hexdigest() +LOCK_HASH = hashlib.sha256(LOCK.read_bytes()).hexdigest() +app = modal.App("openboost-v1-comparator-preflight") +image = ( + modal.Image.from_registry( + "nvidia/cuda@sha256:14c54fad24b376ab78a70e1ef6595a2b7c8cdbf187e4f9b76de99a926fb62460", + add_python="3.12", + ) + .uv_pip_install(requirements=[str(LOCK)], extra_options="--require-hashes", uv_version="0.12.1") + .add_local_file(SOURCE, "/opt/capability_smoke.py", copy=True) + .env({"OMP_NUM_THREADS": "2", "OPENBLAS_NUM_THREADS": "2"}) +) + + +# The standard wheel lacks CUDA: rebuild the same hash-locked release explicitly. +image = ( + image.apt_install("build-essential", "libboost-dev") + .env({"CC": "gcc", "CXX": "g++", "CMAKE_BUILD_PARALLEL_LEVEL": "2"}) + .uv_pip_install( + requirements=[str(LOCK)], + extra_options="--require-hashes --reinstall-package lightgbm --no-binary lightgbm --config-settings=cmake.define.USE_CUDA=ON --config-settings=cmake.define.CMAKE_CUDA_ARCHITECTURES=75", + uv_version="0.12.1", + ) +) + + +@app.function( + image=image, + gpu="T4", + cpu=2, + memory=8192, + timeout=1800, + retries=0, + max_containers=1, + serialized=True, + include_source=False, +) +def preflight(): + import json + import subprocess + import sys + import tempfile + from pathlib import Path + + import cupy as cp + + results = {} + code = "import importlib.util,json,sys; spec=importlib.util.spec_from_file_location('smoke','/opt/capability_smoke.py'); m=importlib.util.module_from_spec(spec); spec.loader.exec_module(m); r=m.run(*sys.argv[1:4]); open(sys.argv[4],'w').write(json.dumps(r))" + for device in ["cpu", "cuda"]: + cells = [] + environment = {} + for library in ["xgboost", "lightgbm", "catboost"]: + for application in [f"A{i}" for i in range(1, 12)]: + with tempfile.TemporaryDirectory() as temp: + output = Path(temp) / "result.json" + try: + worker = subprocess.run( + [sys.executable, "-c", code, device, library, application, str(output)], + capture_output=True, + text=True, + timeout=90, + ) + if worker.returncode != 0 or not output.exists(): + cells.append( + { + "library": library, + "application": application, + "device": device, + "status": "error", + "exit_code": worker.returncode, + "reason": "native/worker failure", + "log": worker.stdout + worker.stderr, + } + ) + else: + payload = json.loads(output.read_text()) + environment = payload["environment"] + for cell in payload["cells"]: + cell["worker_exit_code"] = worker.returncode + cell["worker_log"] = worker.stdout + worker.stderr + cells.append(cell) + except subprocess.TimeoutExpired: + cells.append( + { + "library": library, + "application": application, + "device": device, + "status": "timeout", + "reason": "90-second per-cell wall limit", + } + ) + print(device, library, application, cells[-1]["status"], flush=True) + results[device] = {"cells": cells, "environment": environment} + results.update( + gpu_inventory=subprocess.check_output( + ["nvidia-smi", "--query-gpu=name,driver_version,memory.total", "--format=csv,noheader"], + text=True, + ), + cuda_runtime=cp.cuda.runtime.runtimeGetVersion(), + cuda_driver=cp.cuda.runtime.driverGetVersion(), + ) + return results + + +@app.local_entrypoint() +def main(): + result = preflight.remote() + result.update( + source_sha=SOURCE_SHA, + dirty=DIRTY, + source_file_sha256=HASH, + lock_sha256=LOCK_HASH, + scope="comparator capability preflight only", + ) + path = ROOT / "benchmarks/v1/evidence/modal-capabilities-isolated.json" + path.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=False) + "\n") + for device in ["cpu", "cuda"]: + print( + device, [(c["library"], c["application"], c["status"]) for c in result[device]["cells"]] + ) + if any( + c["status"] in {"error", "timeout"} for d in ["cpu", "cuda"] for c in result[d]["cells"] + ): + raise RuntimeError("preflight contains failed cells; inspect saved results") + + +@app.function( + image=image, + gpu="T4", + cpu=2, + memory=8192, + timeout=600, + retries=0, + max_containers=1, + serialized=True, + include_source=False, +) +def pyboost_job(): + import tempfile + import traceback + from pathlib import Path + + import cupy as cp + import numpy as np + import py_boost + from py_boost import GradientBoosting + + rng = np.random.default_rng(73) + x = rng.normal(size=(96, 5)).astype(np.float32) + weights = np.linspace(0.5, 2, 96, dtype=np.float32) + cells = [] + for outputs in [1, 2]: + try: + y = np.column_stack([x[:, 0] + i * x[:, 1] for i in range(outputs)]).astype(np.float32) + model = GradientBoosting( + "mse", ntrees=4, lr=0.1, max_depth=2, min_data_in_leaf=2, seed=73, verbose=10 + ) + model.fit(x, y, sample_weight=weights) + before = model.predict(x) + with tempfile.TemporaryDirectory() as temp: + path = str(Path(temp) / "model.json") + model.dump(path) + loaded = GradientBoosting("mse") + loaded.load(path) + after = loaded.predict(x) + if before.shape != (96, outputs) or not np.isfinite(before).all(): + raise ValueError("invalid prediction shape/value") + np.testing.assert_allclose(before, after, rtol=1e-6, atol=1e-7) + cells.append( + { + "outputs": outputs, + "status": "pass", + "reload_max_abs_error": float(np.max(np.abs(before - after))), + } + ) + except Exception: + cells.append({"outputs": outputs, "status": "error", "reason": traceback.format_exc()}) + return { + "cells": cells, + "cuda_runtime": cp.cuda.runtime.runtimeGetVersion(), + "pyboost_path": str(Path(py_boost.__file__).parent.name), + "scope": "weighted scalar/vector GPU fit and JSON reload only", + } + + +@app.local_entrypoint() +def pyboost(): + result = pyboost_job.remote() + result.update( + source_sha=SOURCE_SHA, + dirty=DIRTY, + lock_sha256=LOCK_HASH, + harness_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + ) + (ROOT / "benchmarks/v1/evidence/pyboost-cuda.json").write_text( + json.dumps(result, indent=2, sort_keys=True) + "\n" + ) + print(result["cells"]) + if any(c["status"] != "pass" for c in result["cells"]): + raise RuntimeError("Py-Boost preflight failed") diff --git a/benchmarks/v1/openboost_predict.py b/benchmarks/v1/openboost_predict.py new file mode 100644 index 0000000..e657a96 --- /dev/null +++ b/benchmarks/v1/openboost_predict.py @@ -0,0 +1,89 @@ +"""Fresh-process inference for current CPU evaluation bundles; no training imports.""" + +import argparse +import json +from pathlib import Path + +import numpy as np + +from openboost import NumericData +from openboost.artifacts import Model +from openboost.multioutput import MultiOutputModel, TargetScale +from openboost.objectives import Normal + +OUTPUTS = { + "A1": "mean", + "A2": "positive_class_probability", + "A3": "class_probabilities", + "A11": "normal_mean_scale", + "A6": "multioutput_original_units", +} + + +def predict_saved(saved, x): + if ( + not isinstance(saved, dict) + or set(saved) + != ( + {"format", "application", "output", "model"} + | ({"target_scale"} if saved.get("application") == "A6" else set()) + ) + or saved["format"] != "openboost-evaluation-v1" + or saved["application"] not in OUTPUTS + or saved["output"] != OUTPUTS[saved["application"]] + ): + raise ValueError("unsupported evaluation bundle") + model = Model.from_record(saved["model"]) + scale = None + if saved["application"] == "A6": + record = saved["target_scale"] + if not isinstance(record, dict) or set(record) != {"mean", "std", "constant"}: + raise ValueError("invalid evaluation target scale") + scale = TargetScale(record["mean"], record["std"], record["constant"]) + width = len(scale.mean) if scale is not None else 1 if saved["application"] == "A1" else 2 + classification = saved["application"] in {"A2", "A3"} + if classification: + if model.classes is None: + raise ValueError("classification output requires class schema") + count = len(model.classes.values) + if model.classes.values != tuple(range(count)) or any( + type(v) is not int for v in model.classes.values + ): + raise ValueError("canonical encoded class order required") + if count != 2 if saved["application"] == "A2" else count < 3: + raise ValueError("class count differs from task") + width = 1 if saved["application"] == "A2" else count + if model.base.shape != (width,) or (not classification and model.classes is not None): + raise ValueError("model differs from declared output semantics") + x = np.asarray(x) + if x.ndim != 2 or not np.isfinite(x).all(): + raise ValueError("finite encoded prediction matrix required") + data = NumericData(x, np.arange(len(x)), model.feature_names) + if classification: + probability = model.predict_proba(data) + return probability[:, 1] if saved["application"] == "A2" else probability + if scale is not None: + return MultiOutputModel(model, scale).predict(data) + raw = model.predict(data) + return raw[:, 0] if width == 1 else Normal.parameters(raw) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("model", type=Path) + parser.add_argument("features", type=Path) + parser.add_argument("output", type=Path) + args = parser.parse_args() + with np.load(args.features, allow_pickle=False) as arrays: + if set(arrays.files) != {"x", "row_ids"}: + raise ValueError("prediction packet must contain only features and row IDs") + x, ids = arrays["x"], arrays["row_ids"] + if ids.ndim != 1 or len(ids) != len(x) or len(np.unique(ids)) != len(ids): + raise ValueError("unique aligned prediction row IDs required") + saved = json.loads(args.model.read_text()) + prediction = predict_saved(saved, x) + np.savez(args.output, row_ids=ids, prediction=prediction) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/openboost_worker.py b/benchmarks/v1/openboost_worker.py new file mode 100644 index 0000000..21ae1d7 --- /dev/null +++ b/benchmarks/v1/openboost_worker.py @@ -0,0 +1,167 @@ +"""Current CPU A1/A2/A3/A6/A11 trials on frozen encoded train/validation packets. + +Explicit validation targets are required even with fixed budgets. The caller +controls process threads and resource limits. Test arrays are always rejected. +""" + +import argparse +import json +from dataclasses import asdict +from pathlib import Path + +import numpy as np + +from openboost import ClassSchema, NumericData, Problem, RunContext +from openboost.multioutput import TargetScale +from openboost.recipes import binary, multi_squared, multiclass, normal, squared + +if __package__: + from benchmarks.v1.openboost_predict import OUTPUTS, predict_saved + from benchmarks.v1.preprocessing import fit_target_scale +else: + from openboost_predict import OUTPUTS, predict_saved + from preprocessing import fit_target_scale + + +def fit(job, arrays): + classification = job.get("application") in {"A2", "A3"} + required = {"application", "library", "device", "seed", "threads", "config"} + if ( + not required <= set(job) + or set(job) + - required + - {"early_stopping_rounds", "input_npz"} + - ({"classes"} if classification else set()) + or job["application"] not in OUTPUTS + or job["library"] != "openboost" + or job["device"] != "cpu" + ): + raise ValueError("unsupported current OpenBoost job") + if type(job["threads"]) is not int or job["threads"] != 1: + raise ValueError("current worker requires one process thread") + if type(job["seed"]) is not int or job["seed"] < 0: + raise ValueError("nonnegative integer seed required") + needed = {"x_train", "y_train", "x_validation", "y_validation", "validation_row_ids"} + if not needed <= set(arrays) or set(arrays) - needed - {"weight_train", "weight_validation"}: + raise ValueError("explicit train/validation arrays only; no test arrays") + external_ids = np.asarray(arrays["validation_row_ids"]) + if ( + external_ids.ndim != 1 + or external_ids.dtype.kind not in "iuUS" + or len(external_ids) != len(arrays["x_validation"]) + or len(np.unique(external_ids)) != len(external_ids) + ): + raise ValueError("unique aligned external validation row IDs required") + cfg = dict(job["config"]) + if cfg.pop("seed_from_fold", True) is not True: + raise ValueError("seed semantics differ") + allowed = {"rounds", "learning_rate", "max_depth", "reg_lambda", "bins"} + if job["application"] == "A11": + allowed |= {"mode", "damping", "minimum_scale"} + if job["application"] == "A6": + allowed |= {"mode"} + if set(cfg) - allowed or not {"rounds", "learning_rate"} <= set(cfg): + raise ValueError("unsupported current recipe config") + if type(cfg["rounds"]) is not int or cfg["rounds"] <= 0: + raise ValueError("positive round budget required") + classes = None + if classification: + count = job.get("classes") + if type(count) is not int or (count != 2 if job["application"] == "A2" else count < 3): + raise ValueError("explicit canonical classification count required") + classes = ClassSchema(tuple(range(count))) + problems = [] + width = 1 if job["application"] == "A1" else 2 + if classification: + width = 1 if job["application"] == "A2" else count + multi = job["application"] == "A6" + target_scale = None + scale = None + names = None + for part in ("train", "validation"): + x, y = np.asarray(arrays["x_" + part]), np.asarray(arrays["y_" + part]) + if ( + x.ndim != 2 + or ( + y.ndim != 2 or not y.shape[1] or len(y) != len(x) if multi else y.shape != (len(x),) + ) + or not len(x) + or not np.isfinite(x).all() + or not np.isfinite(y).all() + ): + raise ValueError("finite encoded inputs and aligned task targets required") + if multi and part == "train": + width = y.shape[1] + target_scale = fit_target_scale(y) + scale = TargetScale(target_scale["mean"], target_scale["std"], target_scale["constant"]) + names = tuple(f"x{i}" for i in range(x.shape[1])) if names is None else names + # Packet IDs remain in emitted artifacts; public data uses local integer rows. + ids = np.arange(len(x)) + data = NumericData(x, ids, names) + problems.append( + Problem( + data, + y if multi else y[:, None], + data.row_ids, + weight=arrays.get("weight_" + part), + raw_width=width, + classes=classes, + ) + ) + if multi: + problems = [scale.transform(p) for p in problems] + recipe = {"A1": squared, "A2": binary, "A3": multiclass, "A6": multi_squared, "A11": normal}[ + job["application"] + ] + result = recipe( + *problems, + context=RunContext("evaluation", job["seed"]), + patience=job.get("early_stopping_rounds"), + **cfg, + ) + selection = "final" if job.get("early_stopping_rounds") is None else "best_validation" + model = result.state.model if selection == "final" else result.state.best_model + saved = dict( + format="openboost-evaluation-v1", + application=job["application"], + output=OUTPUTS[job["application"]], + model=model.record(), + ) + if multi: + saved["target_scale"] = target_scale + prediction = predict_saved(saved, arrays["x_validation"]) + training = dict( + selection=selection, + stop={**asdict(result.stop), "reason": result.stop.reason}, + accepted_commits=result.state.version, + selected_model_identity=model.identity, + best_validation_score=result.state.best_score, + output=saved["output"], + ) + if classification: + training.update(class_order=list(classes.values), selection_metric="logloss") + if multi: + training.update( + target_scale=target_scale, + scale_convention="unweighted_train_population", + selection_metric="row_mean_sum_standardized_half_squared_error", + ) + return prediction, saved, training + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("job", type=Path) + args = parser.parse_args() + job = json.loads(args.job.read_text()) + with np.load(job["input_npz"], allow_pickle=False) as data: + arrays = {name: data[name] for name in data.files} + prediction, saved, training = fit(job, arrays) + # model.bin is UTF-8 JSON, not pickle; the process runner requires this filename. + Path("model.bin").write_text(json.dumps(saved, allow_nan=False) + "\n") + np.savez("predictions.npz", row_ids=arrays["validation_row_ids"], prediction=prediction) + Path("training.json").write_text(json.dumps(training, indent=2, allow_nan=False) + "\n") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/openboost_worker_smoke.py b/benchmarks/v1/openboost_worker_smoke.py new file mode 100644 index 0000000..de5b970 --- /dev/null +++ b/benchmarks/v1/openboost_worker_smoke.py @@ -0,0 +1,181 @@ +"""Bounded current A1/A2/A3/A6/A11 worker integration on all five frozen folds.""" + +import argparse +import hashlib +import importlib.metadata +import json +import os +import platform +import subprocess +import sys +from pathlib import Path + +import numpy as np + +from benchmarks.v1.process_runner import execute +from benchmarks.v1.worker_data import export + + +def run(directory, applications=("A1", "A6", "A11")): + if ( + not applications + or len(set(applications)) != len(applications) + or set(applications) - {"A1", "A2", "A3", "A6", "A11"} + ): + raise ValueError("unique supported applications required") + root = Path(directory).resolve() + root.mkdir(parents=True, exist_ok=True) + if any(root.iterdir()): + raise ValueError("fresh output directory required") + repo = Path(__file__).resolve().parents[2] + report = dict( + scope="Current A1/A2/A3/A6/A11 real-data validation plumbing only; four rounds, no test scores, quality or performance claim", + revision=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + argv=[ + sys.executable, + "-m", + "benchmarks.v1.openboost_worker_smoke", + str(root), + "--applications", + *applications, + ], + python=platform.python_version(), + os=platform.platform(), + machine=platform.machine(), + cpu_count=os.cpu_count(), + device="cpu", + gpu=None, + threads=1, + memory_cap=None, + packages={name: importlib.metadata.version(name) for name in ("numpy", "openboost")}, + sources={ + str(p.relative_to(repo)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in sorted((repo / "src/openboost").rglob("*.py")) + }, + data={}, + cells=[], + ) + for name in ( + "openboost_worker.py", + "openboost_predict.py", + "openboost_worker_smoke.py", + "worker_data.py", + "preprocessing.py", + "process_runner.py", + ): + p = Path(__file__).with_name(name) + report["sources"][str(p.relative_to(repo))] = hashlib.sha256(p.read_bytes()).hexdigest() + for app in applications: + packet = root / app + manifest = export(app, packet) + report["data"][app] = manifest + for fold in manifest["folds"]: + seed = fold["seed"] + job = dict( + application=app, + library="openboost", + device="cpu", + threads=1, + seed=seed, + early_stopping_rounds=3, + config=dict(rounds=4, learning_rate=0.1, max_depth=2, reg_lambda=1, bins=32), + input_npz=str(packet / fold["artifacts"]["worker-input"]["path"]), + ) + if app in {"A2", "A3"}: + job["classes"] = 2 if app == "A2" else 7 + job_path = packet / str(seed) / "job.json" + job_path.write_text(json.dumps(job, indent=2) + "\n") + output = packet / str(seed) / "fit" + record = execute( + [sys.executable, str(repo / "benchmarks/v1/openboost_worker.py"), str(job_path)], + output, + timeout_s=90, + threads=1, + ) + record.update(application=app, fold=seed, job=job) + if record["status"] == "pass": + try: + with np.load(job["input_npz"], allow_pickle=False) as arrays: + np.savez( + output / "features.npz", + x=arrays["x_validation"], + row_ids=arrays["validation_row_ids"], + ) + command = [ + sys.executable, + str(repo / "benchmarks/v1/openboost_predict.py"), + str(output / "model.bin"), + str(output / "features.npz"), + str(output / "replay.npz"), + ] + record["replay_command"] = command + replay = subprocess.run( + command, + cwd=output, + capture_output=True, + text=True, + timeout=30, + env=dict( + os.environ, + OMP_NUM_THREADS="1", + OPENBLAS_NUM_THREADS="1", + MKL_NUM_THREADS="1", + ), + ) + if replay.returncode: + raise RuntimeError(replay.stderr) + with ( + np.load(output / "predictions.npz") as actual, + np.load(output / "replay.npz") as restored, + ): + np.testing.assert_array_equal(actual["row_ids"], restored["row_ids"]) + np.testing.assert_array_equal(actual["prediction"], restored["prediction"]) + expected_width = ( + () if app in {"A1", "A2"} else (7,) if app == "A3" else (2,) + ) + if app == "A6": + expected_width = (len(fold["metadata"]["target_scale"]["mean"]),) + assert actual["prediction"].shape == ( + len(actual["row_ids"]), + *expected_width, + ) + assert np.isfinite(actual["prediction"]).all() + if app == "A11": + assert (actual["prediction"][:, 1] > 0).all() + if app in {"A2", "A3"}: + assert ((actual["prediction"] >= 0) & (actual["prediction"] <= 1)).all() + if app == "A3": + np.testing.assert_allclose(actual["prediction"].sum(axis=1), 1.0) + record["prediction_shape"] = list(actual["prediction"].shape) + record["fresh_process_exact"] = True + record["training"] = json.loads((output / "training.json").read_text()) + if app == "A6": + assert ( + record["training"]["target_scale"] == fold["metadata"]["target_scale"] + ) + record["replay_sha256"] = hashlib.sha256( + (output / "replay.npz").read_bytes() + ).hexdigest() + except Exception as error: + record.update(status="error", reason=str(error)) + report["cells"].append(record) + (root / "summary.json").write_text(json.dumps(report, indent=2, allow_nan=False) + "\n") + return report + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + parser.add_argument( + "--applications", + nargs="+", + choices=("A1", "A2", "A3", "A6", "A11"), + default=["A1", "A6", "A11"], + ) + args = parser.parse_args() + result = run(args.directory, args.applications) + passed = sum(c["status"] == "pass" for c in result["cells"]) + print(f"{passed}/{len(result['cells'])} current real-data worker cells passed") + if passed != len(result["cells"]): + raise SystemExit(1) diff --git a/benchmarks/v1/parametric.py b/benchmarks/v1/parametric.py new file mode 100644 index 0000000..662ea9f --- /dev/null +++ b/benchmarks/v1/parametric.py @@ -0,0 +1,187 @@ +"""CPU parametric comparison controls with explicit exposure and structure. + +These are evaluation adapters, not OpenBoost production recipes. Objects are +trusted local Python bundles; model inference needs the pinned sklearn/SciPy stack. +""" + +import warnings + +import numpy as np + + +def _inputs(x, y, weight): + x, y = np.asarray(x, dtype=float), np.asarray(y, dtype=float) + w = np.ones(len(y)) if weight is None else np.asarray(weight, dtype=float) + if x.ndim != 2 or y.ndim != 1 or len(x) != len(y) or not len(y): + raise ValueError("aligned nonempty scalar targets and matrix required") + if not np.isfinite(x).all() or not np.isfinite(y).all(): + raise ValueError("finite inputs required") + if w.shape != y.shape or not np.isfinite(w).all() or np.any(w < 0) or w.sum() <= 0: + raise ValueError("invalid business weights") + return x, y, w + + +def _exposure(exposure, rows): + e = np.asarray(exposure, dtype=float) + if e.shape != (rows,) or not np.isfinite(e).all() or np.any(e <= 0): + raise ValueError("positive finite exposure required") + return e + + +def fit_glm(task, x, target, *, exposure=None, weight=None, alpha=0.1, max_iter=1000): + """Fit counts (A7), individual positive payments (A8), or period totals (A9).""" + from sklearn.exceptions import ConvergenceWarning + from sklearn.linear_model import GammaRegressor, PoissonRegressor, TweedieRegressor + from sklearn.preprocessing import StandardScaler + + if task not in ["A7", "A8", "A9"] or not np.isfinite(alpha) or alpha < 0: + raise ValueError("invalid GLM task/penalty") + if type(max_iter) is not int or max_iter <= 0: + raise ValueError("positive convergence budget required") + x, y, w = _inputs(x, target, weight) + if np.any(y < 0) or (task == "A8" and np.any(y <= 0)): + raise ValueError("invalid positive-family target") + if task == "A7" and np.any(y != np.floor(y)): + raise ValueError("integer counts required") + if task == "A8": + if exposure is not None: + raise ValueError("severity has no exposure adjustment") + model = GammaRegressor(alpha=alpha, max_iter=max_iter) + else: + e = _exposure(exposure, len(y)) + y, w = y / e, w * e + model = ( + PoissonRegressor(alpha=alpha, max_iter=max_iter) + if task == "A7" + else TweedieRegressor(power=1.5, link="log", alpha=alpha, max_iter=max_iter) + ) + if np.dot(y, w) <= 0: + raise ValueError("positive weighted target mass required for log-link fit") + scaler = StandardScaler().fit(x) + with warnings.catch_warnings(): + warnings.simplefilter("error", ConvergenceWarning) + model.fit(scaler.transform(x), y, sample_weight=w) + return dict(task=task, scaler=scaler, model=model) + + +def predict_glm(saved, x, exposure=None): + x = np.asarray(x, dtype=float) + if x.ndim != 2 or not np.isfinite(x).all(): + raise ValueError("finite feature matrix required") + rate = saved["model"].predict(saved["scaler"].transform(x)) + if not np.isfinite(rate).all() or np.any(rate <= 0): + raise ValueError("invalid log-link mean") + if saved["task"] == "A8": + if exposure is not None: + raise ValueError("severity has no exposure adjustment") + return {"mean": rate} + e = _exposure(exposure, len(x)) + return {"annualized": rate, "period": e * rate} + + +def fit_paid_composition( + x, + paid_count, + period_total, + exposure, + claim_policy, + claim_amount, + *, + weight=None, + count_alpha=0.1, + severity_alpha=0.1, + max_iter=1000, +): + """Use exactly the positive-payment records underlying each policy's total.""" + x, counts, w = _inputs(x, paid_count, weight) + totals = np.asarray(period_total, dtype=float) + index, amounts = np.asarray(claim_policy), np.asarray(claim_amount, dtype=float) + if totals.shape != counts.shape or not np.isfinite(totals).all() or np.any(totals < 0): + raise ValueError("invalid period totals") + if ( + index.ndim != 1 + or index.dtype.kind not in "iu" + or amounts.shape != index.shape + or not len(index) + ): + raise ValueError("aligned paid-claim policy indices required") + if ( + np.any(index < 0) + or np.any(index >= len(x)) + or not np.isfinite(amounts).all() + or np.any(amounts <= 0) + ): + raise ValueError("orphan or nonpositive paid claim") + actual_counts = np.bincount(index, minlength=len(x)) + actual_totals = np.bincount(index, weights=amounts, minlength=len(x)) + if not np.array_equal(actual_counts, counts) or not np.allclose( + actual_totals, totals, rtol=1e-12, atol=1e-8 + ): + raise ValueError("counts/totals must come from these positive-payment records") + frequency = fit_glm( + "A7", x, counts, exposure=exposure, weight=w, alpha=count_alpha, max_iter=max_iter + ) + severity = fit_glm( + "A8", x[index], amounts, weight=w[index], alpha=severity_alpha, max_iter=max_iter + ) + return dict(frequency=frequency, severity=severity) + + +def predict_paid_composition(saved, x, exposure): + counts = predict_glm(saved["frequency"], x, exposure) + severity = predict_glm(saved["severity"], x)["mean"] + return { + "annualized": counts["annualized"] * severity, + "period": counts["period"] * severity, + "paid_count": counts["period"], + "severity": severity, + } + + +def fit_global_formula( + age, target, *, weight=None, initial_amplitude_multiplier=1.0, initial_rate=1.0, max_nfev=2000 +): + """Fit a global positive a,b in a*(1-exp(-b*age)); age is already days/28.""" + from scipy.optimize import least_squares + + age = np.asarray(age, dtype=float) + _, y, w = _inputs(age[:, None], target, weight) + if np.any(age <= 0) or np.any(y < 0) or np.dot(y, w) <= 0: + raise ValueError("positive age and positive weighted target mass required") + if ( + not np.isfinite([initial_amplitude_multiplier, initial_rate]).all() + or min(initial_amplitude_multiplier, initial_rate) <= 0 + ): + raise ValueError("positive formula initialization required") + if type(max_nfev) is not int or max_nfev <= 0: + raise ValueError("positive optimizer budget required") + start = np.array( + [max(float(np.average(y, weights=w)), 1e-6) * initial_amplitude_multiplier, initial_rate] + ) + raw = start + np.log(-np.expm1(-start)) + + def residual(theta): + a, b = np.logaddexp(0, theta) + return np.sqrt(w) * (a * -np.expm1(-b * age) - y) + + fitted = least_squares(residual, raw, max_nfev=max_nfev) + a, b = np.logaddexp(0, fitted.x) + if not fitted.success or not np.isfinite([a, b, fitted.cost]).all() or min(a, b) <= 0: + raise ValueError("formula optimizer did not converge to finite positive parameters") + return dict( + amplitude=float(a), + rate=float(b), + nfev=fitted.nfev, + train_age_min=float(age.min()), + train_age_max=float(age.max()), + ) + + +def predict_global_formula(saved, age): + age = np.asarray(age, dtype=float) + if age.ndim != 1 or not np.isfinite(age).all() or np.any(age <= 0): + raise ValueError("positive finite age vector required") + a, b = saved["amplitude"], saved["rate"] + if not np.isfinite([a, b]).all() or min(a, b) <= 0: + raise ValueError("invalid formula state") + return a * -np.expm1(-b * age) diff --git a/benchmarks/v1/parametric_smoke.py b/benchmarks/v1/parametric_smoke.py new file mode 100644 index 0000000..dba3d41 --- /dev/null +++ b/benchmarks/v1/parametric_smoke.py @@ -0,0 +1,88 @@ +"""Hand-check exposure-weighted GLM/composition controls and formula persistence.""" + +import hashlib +import importlib.metadata +import json +import pickle +import subprocess +from pathlib import Path + +import numpy as np + +from benchmarks.v1.parametric import ( + fit_glm, + fit_global_formula, + fit_paid_composition, + predict_glm, + predict_global_formula, + predict_paid_composition, +) + +if __name__ == "__main__": + x = np.ones((3, 2)) + counts = np.array([1, 2, 0]) + totals = np.array([10.0, 60.0, 0.0]) + e, w = np.array([1.0, 2.0, 3.0]), np.array([1.0, 3.0, 2.0]) + index, amount = np.array([0, 1, 1]), np.array([10.0, 20.0, 40.0]) + rows = [] + for task, inputs, target, weight, exposure in [ + ("A7", x, counts, w, e), + ("A8", x[index], amount, w[index], None), + ("A9", x, totals, w, e), + ]: + saved = fit_glm(task, inputs, target, weight=weight, exposure=exposure, alpha=0.0) + p = predict_glm(saved, inputs, exposure) + replay = predict_glm(pickle.loads(pickle.dumps(saved)), inputs, exposure) + for k in p: + np.testing.assert_array_equal(p[k], replay[k]) + expected = np.dot(weight, target) / ( + weight.sum() if exposure is None else np.dot(weight, exposure) + ) + actual = p["mean"] if task == "A8" else p["annualized"] + np.testing.assert_allclose(actual, expected, rtol=1e-4, atol=1e-8) + rows.append( + dict(task=task, status="pass", expected_mean=float(expected), actual=actual.tolist()) + ) + saved = fit_paid_composition( + x, counts, totals, e, index, amount, weight=w, count_alpha=0.0, severity_alpha=0.0 + ) + p = predict_paid_composition(saved, x, e) + replay = predict_paid_composition(pickle.loads(pickle.dumps(saved)), x, e) + expected = np.dot(w, totals) / np.dot(w, e) + np.testing.assert_allclose(p["annualized"], expected, rtol=1e-4) + doubled = predict_paid_composition(saved, x, 2 * e) + np.testing.assert_array_equal(doubled["annualized"], p["annualized"]) + np.testing.assert_array_equal(doubled["period"], 2 * p["period"]) + for k in p: + np.testing.assert_array_equal(p[k], replay[k]) + rows.append( + dict( + task="A9 paid-count times severity", + status="pass", + expected_annualized=float(expected), + prediction=p["annualized"].tolist(), + ) + ) + age = np.linspace(0.1, 4, 40) + saved = fit_global_formula(age, 3 * -np.expm1(-0.7 * age)) + reloaded = json.loads(json.dumps(saved)) + np.testing.assert_array_equal( + predict_global_formula(saved, age), predict_global_formula(reloaded, age) + ) + np.testing.assert_allclose([saved["amplitude"], saved["rate"]], [3.0, 0.7], rtol=1e-6) + rows.append(dict(task="A12 global formula", status="pass", parameters=saved)) + result = dict( + scope="synthetic CPU parametric controls, not real A9/A12 quality", + cells=rows, + source_sha=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + packages={d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + source_hashes={ + n: hashlib.sha256(Path(__file__).with_name(n).read_bytes()).hexdigest() + for n in ["parametric.py", "parametric_smoke.py"] + }, + ) + Path("benchmarks/v1/evidence/parametric-cpu.json").write_text( + json.dumps(result, indent=2) + "\n" + ) + print(rows) diff --git a/benchmarks/v1/parametric_worker.py b/benchmarks/v1/parametric_worker.py new file mode 100644 index 0000000..4dbfcb1 --- /dev/null +++ b/benchmarks/v1/parametric_worker.py @@ -0,0 +1,107 @@ +"""One validation-only GLM, paid-loss composition or global-formula trial.""" + +import argparse +import json +import pickle +from pathlib import Path + +import numpy as np + +if __package__: + from benchmarks.v1 import parametric as controls + from benchmarks.v1.judge import read_json +else: + import parametric as controls + from judge import read_json + + +def fit(job, arrays): + if set(job) != {"application", "method", "config", "input_npz"}: + raise ValueError("unsupported parametric job fields") + task, method, config = job["application"], job["method"], job["config"] + required = {"y_train", "validation_row_ids"} + optional = {"weight_train"} + if method == "formula_global" and task == "A12": + required.update({"age_train", "age_validation"}) + expected = {"initial_amplitude_multiplier", "initial_rate", "max_nfev"} + elif method in {"glm", "paid_composition"}: + required.update({"x_train", "x_validation"}) + expected = {"alpha", "max_iter"} + if task in ["A7", "A9"]: + required.update({"exposure_train", "exposure_validation"}) + if method == "paid_composition": + if task != "A9": + raise ValueError("paid composition requires A9") + required.update({"paid_count", "claim_policy", "claim_amount"}) + expected = {"count_alpha", "severity_alpha", "max_iter"} + else: + raise ValueError("unsupported parametric method/task") + if set(config) != expected or not required <= set(arrays) or set(arrays) - required - optional: + raise ValueError("unsupported/missing input or config fields") + ids = arrays["validation_row_ids"] + size = len(arrays["age_validation"] if method == "formula_global" else arrays["x_validation"]) + if ids.ndim != 1 or len(ids) != size or len(np.unique(ids)) != size: + raise ValueError("invalid validation row IDs") + weight = arrays.get("weight_train") + if method == "formula_global": + model = controls.fit_global_formula( + arrays["age_train"], arrays["y_train"], weight=weight, **config + ) + elif method == "paid_composition": + model = controls.fit_paid_composition( + arrays["x_train"], + arrays["paid_count"], + arrays["y_train"], + arrays["exposure_train"], + arrays["claim_policy"], + arrays["claim_amount"], + weight=weight, + **config, + ) + else: + model = controls.fit_glm( + task, + arrays["x_train"], + arrays["y_train"], + exposure=arrays.get("exposure_train"), + weight=weight, + **config, + ) + saved = dict(application=task, method=method, model=model) + prediction = predict(saved, arrays) + if not np.isfinite(prediction).all() or prediction.shape != (size,): + raise ValueError("invalid scalar predictions") + return prediction, saved + + +def predict(saved, arrays): + method, task, model = saved["method"], saved["application"], saved["model"] + if method == "formula_global": + return controls.predict_global_formula(model, arrays["age_validation"]) + if method == "paid_composition": + return controls.predict_paid_composition( + model, arrays["x_validation"], arrays["exposure_validation"] + )["annualized"] + result = controls.predict_glm(model, arrays["x_validation"], arrays.get("exposure_validation")) + return result["mean" if task == "A8" else "period" if task == "A7" else "annualized"] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("job", type=Path) + args = parser.parse_args() + job = read_json(args.job.read_bytes()) + with np.load(job["input_npz"], allow_pickle=False) as data: + arrays = {k: data[k] for k in data.files} + prediction, model = fit(job, arrays) + raw = pickle.dumps(model) + np.testing.assert_allclose(prediction, predict(pickle.loads(raw), arrays), rtol=1e-7, atol=1e-8) + np.savez("predictions.npz", row_ids=arrays["validation_row_ids"], prediction=prediction) + Path("model.bin").write_bytes(raw) + Path("training.json").write_text( + json.dumps( + {"application": job["application"], "method": job["method"], "config": job["config"]}, + indent=2, + ) + + "\n" + ) diff --git a/benchmarks/v1/parametric_worker_smoke.py b/benchmarks/v1/parametric_worker_smoke.py new file mode 100644 index 0000000..4770a75 --- /dev/null +++ b/benchmarks/v1/parametric_worker_smoke.py @@ -0,0 +1,101 @@ +"""Run every new parametric control through the bounded validation worker CLI.""" + +import argparse +import hashlib +import json +import subprocess +import sys +from pathlib import Path + +import numpy as np + +from benchmarks.v1.process_runner import execute + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + args = parser.parse_args() + root = args.directory.resolve() + root.mkdir(parents=True, exist_ok=True) + if any(root.iterdir()): + raise ValueError("fresh output directory required") + source = Path(__file__).with_name("parametric_worker.py").resolve() + e, w = np.array([1.0, 2.0, 3.0]), np.array([1.0, 3.0, 2.0]) + x = np.ones((3, 2)) + cells = [] + for app, method in [ + ("A7", "glm"), + ("A8", "glm"), + ("A9", "glm"), + ("A9", "paid_composition"), + ("A12", "formula_global"), + ]: + arrays = dict(x_train=x, x_validation=x, validation_row_ids=np.arange(3, 6), weight_train=w) + config = dict(alpha=0.0, max_iter=1000) + if app == "A7": + arrays.update(y_train=np.array([1, 2, 0]), exposure_train=e, exposure_validation=2 * e) + expected = 2 * e * 7 / 13 + elif app == "A8": + arrays.update( + y_train=np.array([10.0, 20.0, 40.0]), weight_train=np.array([1.0, 3.0, 3.0]) + ) + expected = np.full(3, 190 / 7) + elif app == "A9": + arrays.update( + y_train=np.array([10.0, 60.0, 0.0]), exposure_train=e, exposure_validation=2 * e + ) + expected = np.full(3, 190 / 13) + if method == "paid_composition": + arrays.update( + paid_count=np.array([1, 2, 0]), + claim_policy=np.array([0, 1, 1]), + claim_amount=np.array([10.0, 20.0, 40.0]), + ) + config = dict(count_alpha=0.0, severity_alpha=0.0, max_iter=1000) + else: + age = np.linspace(0.1, 4, 40) + valid = np.array([0.2, 1.0, 3.0]) + arrays = dict( + age_train=age, + y_train=3 * -np.expm1(-0.7 * age), + age_validation=valid, + validation_row_ids=np.arange(40, 43), + ) + config = dict(initial_amplitude_multiplier=1.0, initial_rate=1.0, max_nfev=2000) + expected = 3 * -np.expm1(-0.7 * valid) + stem = app + "-" + method + input_path = root / (stem + ".npz") + np.savez(input_path, **arrays) + job = dict(application=app, method=method, config=config, input_npz=str(input_path)) + job_path = root / (stem + ".json") + job_path.write_text(json.dumps(job)) + out = root / stem + result = execute([sys.executable, str(source), str(job_path)], out, timeout_s=60, threads=2) + if result["status"] != "pass": + raise RuntimeError(f"{stem} failed; inspect {out / 'worker.log'}") + with np.load(out / "predictions.npz", allow_pickle=False) as pred: + np.testing.assert_array_equal(pred["row_ids"], arrays["validation_row_ids"]) + np.testing.assert_allclose(pred["prediction"], expected, rtol=1e-4, atol=1e-8) + cells.append( + dict( + task=app, + method=method, + status="pass", + prediction=pred["prediction"].tolist(), + expected=expected.tolist(), + ) + ) + result = dict( + scope="synthetic CPU worker contract checks only", + cells=cells, + source_sha=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + source_hashes={ + n: hashlib.sha256(Path(__file__).with_name(n).read_bytes()).hexdigest() + for n in ["parametric.py", "parametric_worker.py", "parametric_worker_smoke.py"] + }, + ) + Path("benchmarks/v1/evidence/parametric-worker-cpu.json").write_text( + json.dumps(result, indent=2) + "\n" + ) + print(cells) diff --git a/benchmarks/v1/preprocessing.py b/benchmarks/v1/preprocessing.py new file mode 100644 index 0000000..48a6905 --- /dev/null +++ b/benchmarks/v1/preprocessing.py @@ -0,0 +1,101 @@ +"""Train-only dense encoding and output scaling for baseline evaluation adapters.""" + +import numpy as np + + +def fit_encoder(numeric, categories=None): + x = np.asarray(numeric, dtype=float) + if x.ndim != 2 or not len(x) or np.isinf(x).any(): + raise ValueError("numeric matrix with finite values or NaN required") + median = [] + for column in x.T: + observed = column[~np.isnan(column)] + median.append(float(np.median(observed)) if len(observed) else 0.0) + vocab = {} + for name, values in sorted((categories or {}).items()): + a = np.asarray(values, dtype=object) + if a.shape != (len(x),) or any(v is not None and not isinstance(v, str) for v in a): + raise ValueError("aligned string/None categories required") + vocab[name] = sorted({v for v in a if v is not None}) + return { + "numeric_columns": x.shape[1], + "median": median, + "categories": vocab, + "unknown": "missing-indicator", + "numeric_missing_indicators": True, + } + + +def transform(encoder, numeric, categories=None): + x = np.asarray(numeric, dtype=float) + if x.ndim != 2 or x.shape[1] != encoder["numeric_columns"] or np.isinf(x).any(): + raise ValueError("wrong numeric schema") + categories = categories or {} + if set(categories) != set(encoder["categories"]): + raise ValueError("category fields differ from fitted schema") + parts = [np.where(np.isnan(x), encoder["median"], x), np.isnan(x).astype(float)] + for name, vocab in encoder["categories"].items(): + a = np.asarray(categories[name], dtype=object) + if a.shape != (len(x),) or any(v is not None and not isinstance(v, str) for v in a): + raise ValueError("invalid category rows") + lookup = {v: i for i, v in enumerate(vocab)} + codes = np.array([lookup.get(v, len(vocab)) for v in a]) + encoded = np.zeros((len(x), len(vocab) + 1)) + encoded[np.arange(len(x)), codes] = 1 + parts.append(encoded) + return np.concatenate(parts, axis=1) + + +def fit_target_scale(y): + a = np.asarray(y, dtype=float) + if a.ndim != 2 or not len(a) or not np.isfinite(a).all(): + raise ValueError("finite matrix targets required") + std = a.std(axis=0) + return { + "mean": a.mean(axis=0).tolist(), + "std": np.where(std == 0, 1.0, std).tolist(), + "constant": (std == 0).tolist(), + } + + +def censoring_support(time, event): + """Training-only reverse Kaplan-Meier with event-before-censor tie handling. + + At tied times, remove observed events from the censoring risk denominator. + Freeze the step function, its positivity support, and a training-quantile grid. + Actual test evaluation must reject points outside this support. + """ + t = np.asarray(time, dtype=float) + e = np.asarray(event) + if ( + t.ndim != 1 + or e.shape != t.shape + or not len(t) + or not np.isfinite(t).all() + or np.any(t <= 0) + or not np.isin(e, [0, 1]).all() + ): + raise ValueError("invalid observed times/events") + survival = 1.0 + values = [] + times = [] + for point in np.unique(t): + at = t == point + risk = int((t >= point).sum()) - int((at & (e == 1)).sum()) + censored = int((at & (e == 0)).sum()) + if censored: + survival *= 1 - censored / risk + times.append(float(point)) + values.append(survival) + stop = next((times[i] for i, v in enumerate(values) if v == 0), float(t.max())) + grid = np.unique(np.quantile(t, np.linspace(0.1, 0.9, 9))) + grid = grid[(grid < stop) & (grid < float(t.max()))] + if not len(grid): + raise ValueError("no supported censoring evaluation grid") + return { + "times": times, + "survival": values, + "strict_upper": stop, + "grid": grid.tolist(), + "tie_rule": "events removed before censoring risk", + } diff --git a/benchmarks/v1/process_runner.py b/benchmarks/v1/process_runner.py new file mode 100644 index 0000000..e428ce1 --- /dev/null +++ b/benchmarks/v1/process_runner.py @@ -0,0 +1,97 @@ +"""Bounded worker execution with durable logs and explicit failure propagation.""" + +import hashlib +import json +import os +import signal +import subprocess +import time +from pathlib import Path + + +def execute(command, directory, *, timeout_s, threads=2): + """Run one fresh worker; workers must emit predictions.npz and model.bin. + + Files are required before success is possible. This is execution integrity, + not a quality judgment. The caller supplies an empty dedicated output directory. + Memory caps must be enforced by the container/host and recorded separately. + """ + if ( + not isinstance(command, list) + or not command + or any(not isinstance(s, str) or not s for s in command) + ): + raise ValueError("nonempty argv required") + if ( + type(timeout_s) not in (int, float) + or not 0 < timeout_s <= 7200 + or type(threads) is not int + or threads < 1 + ): + raise ValueError("invalid resource budget") + root = Path(directory).resolve() + root.mkdir(parents=True, exist_ok=True) + if any(root.iterdir()): + raise ValueError("worker output directory must be empty") + env = os.environ.copy() + env.update( + { + k: str(threads) + for k in [ + "OMP_NUM_THREADS", + "OPENBLAS_NUM_THREADS", + "MKL_NUM_THREADS", + "NUMBA_NUM_THREADS", + ] + } + ) + started = time.monotonic() + record = { + "command": command, + "timeout_s": timeout_s, + "threads": threads, + "status": "error", + "exit_code": None, + "reason": "", + } + with (root / "worker.log").open("wb") as log: + try: + process = subprocess.Popen( + command, + cwd=root, + env=env, + stdout=log, + stderr=subprocess.STDOUT, + start_new_session=True, + ) + try: + record["exit_code"] = process.wait(timeout=timeout_s) + if record["exit_code"] != 0: + record["reason"] = "worker exited nonzero" + elif not all( + (root / f).is_file() + and not (root / f).is_symlink() + and (root / f).stat().st_size > 0 + for f in ["predictions.npz", "model.bin"] + ): + record["reason"] = "missing or invalid worker artifact" + else: + record["status"] = "pass" + except subprocess.TimeoutExpired: + os.killpg(process.pid, signal.SIGKILL) + process.wait() + record.update( + status="timeout", + exit_code=process.returncode, + reason="wall budget exceeded; process group killed", + ) + except OSError as exc: + record["reason"] = str(exc) + record["wall_s"] = time.monotonic() - started + record["artifacts"] = { + p.name: hashlib.sha256(p.read_bytes()).hexdigest() + for p in root.iterdir() + if p.is_file() and not p.is_symlink() + } + (root / "execution.json").write_text(json.dumps(record, indent=2, sort_keys=True) + "\n") + return record diff --git a/benchmarks/v1/quality.py b/benchmarks/v1/quality.py new file mode 100644 index 0000000..57054cb --- /dev/null +++ b/benchmarks/v1/quality.py @@ -0,0 +1,204 @@ +"""Independent prediction-space metrics and preregistered five-fold E3 comparisons. + +This module does not import OpenBoost or accept producer-supplied quality claims. +Full gate acceptance also requires artifact integrity, row identity, and coverage. +""" + +import math + +import numpy as np + + +def _finite(value): + a = np.asarray(value, dtype=np.float64) + if not a.size or not np.isfinite(a).all(): + raise ValueError("nonempty finite values required") + return a + + +def _log_survival(z): + if z < 8: + return math.log(math.erfc(z / math.sqrt(2)) / 2) + # Asymptotic Mills expansion; switch far enough into the tail for accuracy. + # Use erfc while representable, asymptotics only after underflow. + tail = math.erfc(z / math.sqrt(2)) / 2 + if tail > 0: + return math.log(tail) + inv = 1 / (z * z) + series = 1 - inv + 3 * inv**2 - 15 * inv**3 + 105 * inv**4 + return -0.5 * z * z - 0.5 * math.log(2 * math.pi) - math.log(z) + math.log(series) + + +def metrics( + application, target, prediction, *, weight=None, event=None, query=None, row_ids=None, power=1.5 +): + y, p = _finite(target), _finite(prediction) + if y.ndim not in (1, 2) or p.ndim not in (1, 2) or p.shape[0] != len(y): + raise ValueError("unaligned target/prediction") + w = np.ones(len(y)) if weight is None else _finite(weight) + if w.shape != (len(y),) or np.any(w < 0) or not w.sum() > 0: + raise ValueError("invalid weights") + + def mean(a): + return float(np.dot(w, a) / w.sum()) + + result = {} + with np.errstate(over="raise", divide="raise", invalid="raise"): + if application in ("A1", "A12", "A6"): + if p.shape != y.shape or (application == "A6" and y.ndim != 2): + raise ValueError("wrong regression output schema") + if y.ndim == 1: + result = {"rmse": math.sqrt(mean((p - y) ** 2))} + else: + result = { + f"rmse_{k}": math.sqrt(mean((p[:, k] - y[:, k]) ** 2)) + for k in range(y.shape[1]) + } + elif application == "A2": + if p.shape != y.shape or not np.isin(y, [0, 1]).all() or np.any((p < 0) | (p > 1)): + raise ValueError("binary labels/probabilities required") + # Freeze epsilon for exact boundary probabilities, never accept logits here. + q = np.clip(p, 1e-15, 1 - 1e-15) + result = {"logloss": mean(-y * np.log(q) - (1 - y) * np.log1p(-q))} + elif application == "A3": + if ( + y.ndim != 1 + or p.ndim != 2 + or p.shape[1] < 2 + or np.any(y != np.floor(y)) + or np.any((y < 0) | (y >= p.shape[1])) + ): + raise ValueError("invalid multiclass schema") + if np.any((p < 0) | (p > 1)) or not np.allclose(p.sum(axis=1), 1, rtol=0, atol=1e-7): + raise ValueError("probabilities must sum to one") + result = { + "logloss": mean(-np.log(np.clip(p[np.arange(len(y)), y.astype(int)], 1e-15, 1))) + } + elif application == "A5": + if y.ndim != 1 or p.shape != (len(y), 3): + raise ValueError("three declared quantile columns required") + for k, q in enumerate([0.1, 0.5, 0.9]): + r = y - p[:, k] + result[f"pinball_{q}"] = mean(np.maximum(q * r, (q - 1) * r)) + result["crossing_rate"] = mean(np.any(np.diff(p, axis=1) < 0, axis=1)) + elif application in ("A7", "A8", "A9"): + if p.shape != y.shape or y.ndim != 1 or np.any(p <= 0) or np.any(y < 0): + raise ValueError("nonnegative scalar targets and positive means required") + if application == "A7": + if np.any(y != np.floor(y)): + raise ValueError("integer counts required") + term = np.zeros_like(y) + positive = y > 0 + term[positive] = y[positive] * np.log(y[positive] / p[positive]) + result = {"poisson_deviance": mean(2 * (term - y + p))} + elif application == "A8": + if np.any(y <= 0): + raise ValueError("positive severity required") + ratio = y / p + result = {"gamma_deviance": mean(2 * (ratio - 1 - np.log(ratio)))} + else: + if not 1 < power < 2: + raise ValueError("Tweedie power must lie between 1 and 2") + dev = 2 * ( + y ** (2 - power) / ((1 - power) * (2 - power)) + - y * p ** (1 - power) / (1 - power) + + p ** (2 - power) / (2 - power) + ) + result = {"tweedie_deviance": mean(dev)} + elif application in ("A10", "A11"): + if y.ndim != 1 or p.shape != (len(y), 2) or np.any(p[:, 1] <= 0): + raise ValueError("location and positive standard deviation required") + mu, sigma = p.T + if application == "A11": + z = (y - mu) / sigma + cdf = np.array([0.5 * math.erfc(-v / math.sqrt(2)) for v in z]) + pdf = np.exp(-z * z / 2) / math.sqrt(2 * math.pi) + result = { + "nll": mean(np.log(sigma) + z * z / 2 + 0.5 * math.log(2 * math.pi)), + "crps": mean(sigma * (z * (2 * cdf - 1) + 2 * pdf - 1 / math.sqrt(math.pi))), + "coverage90": mean(np.abs(z) <= 1.6448536269514722), + "width90": mean(2 * 1.6448536269514722 * sigma), + } + else: + e = np.asarray(event) + if np.any(y <= 0) or e.shape != y.shape or not np.isin(e, [0, 1]).all(): + raise ValueError("positive times and event indicators required") + z = (np.log(y) - mu) / sigma + loss = np.log(y) + np.log(sigma) + z * z / 2 + 0.5 * math.log(2 * math.pi) + loss[e == 0] = [-_log_survival(v) for v in z[e == 0]] + result = {"nll": mean(loss)} + elif application == "A4": + q = np.asarray(query) + ids = np.arange(len(y)) if row_ids is None else np.asarray(row_ids) + if ( + p.shape != y.shape + or y.ndim != 1 + or q.shape != y.shape + or ids.shape != y.shape + or len(np.unique(ids)) != len(ids) + ): + raise ValueError("ranking requires aligned query and unique row IDs") + if not np.all(w == 1) or np.any(y < 0) or np.any(y > 4) or np.any(y != np.floor(y)): + raise ValueError("ranking uses unit query weights, relevance 0..4") + values = [] + zero = 0 + for group in np.unique(q): + rows = np.flatnonzero(q == group) + order = rows[np.lexsort((ids[rows], -p[rows]))][:10] + ideal = rows[np.lexsort((ids[rows], -y[rows]))][:10] + discount = np.log2(np.arange(len(order)) + 2) + idcg = np.sum(np.expm1(y[ideal] * math.log(2)) / discount) + if idcg == 0: + values.append(1.0) + zero += 1 + else: + values.append(float(np.sum(np.expm1(y[order] * math.log(2)) / discount) / idcg)) + result = {"ndcg10": float(np.mean(values)), "zero_idcg_queries": float(zero)} + else: + raise ValueError( + "unknown task; A13 must evaluate the validation-selected original task" + ) + if not all(math.isfinite(v) for v in result.values()): + raise ValueError("nonfinite metric") + return result + + +def compare_folds(candidate, baseline, kind): + """Apply E3 to exactly five aligned folds of one primary metric.""" + try: + c, b = _finite(candidate), _finite(baseline) + if c.shape != (5,) or b.shape != (5,): + raise ValueError("exactly five folds required") + if kind == "loss": + if np.any(c < 0) or np.any(b < 0): + raise ValueError("loss ratios require nonnegative values") + near = b <= 1e-8 + ratio = c[~near] / b[~near] + ok = bool(np.all(np.abs(c[near] - b[near]) <= 1e-8)) + if len(ratio): + ok = ok and np.median(ratio) <= 1.05 and np.max(ratio) <= 1.15 + return {"pass": bool(ok), "ratios": ratio.tolist(), "near_perfect": int(near.sum())} + if kind not in ["nll", "ndcg"]: + raise ValueError("unknown metric comparison kind") + difference = c - b if kind == "nll" else b - c + med, worst = (0.02, 0.10) if kind == "nll" else (0.01, 0.03) + return { + "pass": bool(np.median(difference) <= med and np.max(difference) <= worst), + "differences": difference.tolist(), + } + except (ValueError, TypeError, OverflowError) as exc: + return {"pass": False, "reason": str(exc)} + + +def select_validation(records, *, maximize=False, expected=16): + """Strict complete search; test predictions/scores are deliberately not inputs.""" + if len(records) != expected or len({r["id"] for r in records}) != expected: + raise ValueError("missing/duplicate search trial") + if any( + set(r) != {"id", "validation", "status"} + or r["status"] != "pass" + or not math.isfinite(r["validation"]) + for r in records + ): + raise ValueError("failed or invalid search trial") + return min(records, key=lambda r: ((-1 if maximize else 1) * r["validation"], r["id"]))["id"] diff --git a/benchmarks/v1/quality_report.py b/benchmarks/v1/quality_report.py new file mode 100644 index 0000000..c4dc176 --- /dev/null +++ b/benchmarks/v1/quality_report.py @@ -0,0 +1,256 @@ +"""Recompute paired quality comparisons from hashed, row-aligned NPZ artifacts. + +This comparison layer does not certify validation-only model selection or the +full required recipe/device matrix, and therefore never declares E3 complete. +""" + +import argparse +import hashlib +import io +import json +from pathlib import Path + +import numpy as np + +from benchmarks.v1.auxiliary import ( + classification, + normal_pit, + paired_interval, + structure_errors, + survival, +) +from benchmarks.v1.judge import read_json +from benchmarks.v1.preprocessing import fit_target_scale +from benchmarks.v1.quality import compare_folds, metrics + +PRIMARY = { + "A1": ["rmse"], + "A2": ["logloss"], + "A3": ["logloss"], + "A4": ["ndcg10"], + "A5": ["pinball_0.1", "pinball_0.5", "pinball_0.9"], + "A7": ["poisson_deviance"], + "A8": ["gamma_deviance"], + "A9": ["tweedie_deviance"], + "A10": ["nll"], + "A11": ["nll"], + "A12": ["rmse"], +} + + +def load_bytes(root, entry): + if set(entry) != {"path", "sha256"}: + raise ValueError("invalid artifact entry") + rel = Path(entry["path"]) + if rel.is_absolute() or ".." in rel.parts: + raise ValueError("unsafe artifact path") + path = (root / rel).resolve() + if not path.is_relative_to(root): + raise ValueError("artifact escapes root") + raw = path.read_bytes() + if hashlib.sha256(raw).hexdigest() != entry["sha256"]: + raise ValueError("artifact hash mismatch") + return raw + + +def load(root, entry): + with np.load(io.BytesIO(load_bytes(root, entry)), allow_pickle=False) as data: + arrays = {k: data[k] for k in data.files} + return arrays + + +def report(manifest, directory): + result = { + "schema": "openboost-quality-pairs-v1", + "E3_pass": False, + "comparisons": {}, + "errors": [], + "auxiliary_missing": [], + "scope": "paired metrics only; selection provenance and complete recipe/device coverage not certified", + } + root = Path(directory).resolve() + try: + if ( + set(manifest) != {"schema", "cells"} + or manifest["schema"] != result["schema"] + or not manifest["cells"] + ): + raise ValueError("invalid or empty quality matrix") + groups = {} + seen = set() + for cell in manifest["cells"]: + if set(cell) - {"auxiliary"} != { + "application", + "fold", + "kind", + "primary", + "truth", + "candidate", + "baseline", + }: + raise ValueError("invalid comparison cell") + app, fold = cell["application"], cell["fold"] + if ( + app not in {*PRIMARY, "A6"} + or type(fold) is not int + or fold not in range(5) + or (app, fold) in seen + ): + raise ValueError("unknown/duplicate application or fold") + seen.add((app, fold)) + truth = load(root, cell["truth"]) + if ( + "row_ids" not in truth + or "y" not in truth + or set(truth) - {"row_ids", "y", "weight", "event", "query"} + ): + raise ValueError("invalid target schema") + ids = truth["row_ids"] + if ids.ndim != 1 or len(np.unique(ids)) != len(ids) or len(ids) != len(truth["y"]): + raise ValueError("invalid target row IDs") + if app == "A6" and (truth["y"].ndim != 2 or not truth["y"].shape[1]): + raise ValueError("nonempty vector targets required") + primary = ( + [f"rmse_{k}" for k in range(truth["y"].shape[1])] + ["standardized_rmse"] + if app == "A6" + else PRIMARY[app] + ) + kind = "nll" if app in ["A10", "A11"] else "ndcg" if app == "A4" else "loss" + if cell["primary"] != primary or cell["kind"] != kind: + raise ValueError("cannot remove primary metrics or change comparison kind") + auxiliary = cell.get("auxiliary", {}) + if not isinstance(auxiliary, dict): + raise ValueError("auxiliary must be an object") + if app == "A6": + if set(auxiliary) != {"train_rows", "train_targets", "target_scale"}: + raise ValueError("A6 requires training rows, targets and scale") + rows = load(root, auxiliary["train_rows"]) + targets = load(root, auxiliary["train_targets"]) + if set(rows) != {"row_ids"} or set(targets) != {"row_ids", "y"}: + raise ValueError("invalid A6 training schema") + train_ids = rows["row_ids"] + if ( + train_ids.ndim != 1 + or not len(train_ids) + or len(np.unique(train_ids)) != len(train_ids) + or train_ids.dtype.kind not in "iuUS" + or ids.dtype.kind != train_ids.dtype.kind + or not np.array_equal(train_ids, targets["row_ids"]) + or np.intersect1d(train_ids, ids).size + ): + raise ValueError("A6 training row mismatch or evaluation overlap") + if targets["y"].shape != (len(train_ids), truth["y"].shape[1]): + raise ValueError("A6 training target shape mismatch") + scale = read_json(load_bytes(root, auxiliary["target_scale"])) + expected_scale = fit_target_scale(targets["y"]) + if json.dumps(scale, sort_keys=True) != json.dumps(expected_scale, sort_keys=True): + raise ValueError("A6 scale differs from training population") + elif app == "A10" and auxiliary: + if set(auxiliary) != {"censoring"}: + raise ValueError("invalid survival auxiliary entry") + support = read_json(load_bytes(root, auxiliary["censoring"])) + elif app == "A12" and auxiliary: + if set(auxiliary) != {"structure"}: + raise ValueError("invalid structure auxiliary entry") + support = load(root, auxiliary["structure"]) + if ( + set(support) != {"age", "row_ids", "train_min", "train_max"} + or not np.array_equal(support["row_ids"], ids) + or support["train_min"].shape != () + or support["train_max"].shape != () + ): + raise ValueError("invalid structural support arrays") + elif auxiliary: + raise ValueError("unexpected auxiliary entry") + if app in ["A10", "A12"] and not auxiliary: + result["auxiliary_missing"].append({"application": app, "fold": fold}) + scores = [] + for role in ["candidate", "baseline"]: + pred = load(root, cell[role]) + if set(pred) != {"row_ids", "prediction"} or not np.array_equal( + ids, pred["row_ids"] + ): + raise ValueError("prediction row identity mismatch") + kwargs = {k: v for k, v in truth.items() if k not in ["y", "row_ids"]} + measured = metrics(app, truth["y"], pred["prediction"], row_ids=ids, **kwargs) + if app == "A6": + measured["standardized_rmse"] = float( + np.mean([measured[f"rmse_{k}"] / std for k, std in enumerate(scale["std"])]) + ) + elif app in ["A2", "A3"]: + measured = classification( + app, truth["y"], pred["prediction"], truth.get("weight") + ) + elif app == "A11": + measured = normal_pit(truth["y"], pred["prediction"], truth.get("weight")) + elif app == "A10" and auxiliary: + measured = survival( + truth["y"], truth["event"], pred["prediction"], support, truth.get("weight") + ) + elif app == "A12" and auxiliary: + measured["structure_errors"] = structure_errors( + support["age"], + truth["y"], + pred["prediction"], + float(support["train_min"]), + float(support["train_max"]), + truth.get("weight"), + ) + scores.append(measured) + groups.setdefault(app, {})[fold] = (scores, kind, primary) + for app, folds in groups.items(): + if set(folds) != set(range(5)): + raise ValueError(f"{app}: missing fold") + primary = folds[0][2] + kind = folds[0][1] + if any(f[2] != primary or f[1] != kind for f in folds.values()): + raise ValueError("inconsistent fold output schema") + comparisons = { + name: compare_folds( + [folds[f][0][0][name] for f in range(5)], + [folds[f][0][1][name] for f in range(5)], + kind, + ) + for name in primary + } + for name in primary: + comparisons[name]["paired_summary"] = paired_interval( + [folds[f][0][0][name] for f in range(5)], + [folds[f][0][1][name] for f in range(5)], + ) + result["comparisons"][app] = { + "pass": all(c["pass"] for c in comparisons.values()), + "metrics": comparisons, + "fold_metrics": { + str(f): dict(candidate=folds[f][0][0], baseline=folds[f][0][1]) + for f in range(5) + }, + } + except ( + ValueError, + KeyError, + IndexError, + TypeError, + OSError, + OverflowError, + FloatingPointError, + ) as exc: + result["errors"].append(str(exc)) + return result + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("directory", type=Path) + a = p.parse_args() + r = report(read_json((a.directory / "quality-manifest.json").read_bytes()), a.directory) + print(json.dumps(r, indent=2, sort_keys=True, allow_nan=False)) + return int( + bool(r["errors"]) + or not r["comparisons"] + or any(not c["pass"] for c in r["comparisons"].values()) + ) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/v1/ranking.py b/benchmarks/v1/ranking.py new file mode 100644 index 0000000..b8c4f10 --- /dev/null +++ b/benchmarks/v1/ranking.py @@ -0,0 +1,36 @@ +"""Explicit query grouping for baseline rankers; no row-to-pair weight coercion.""" + +import numpy as np + + +def groups(query, rows, weight=None): + q = np.asarray(query) + if q.ndim != 1 or len(q) != rows or rows == 0 or q.dtype.kind not in "iuUS": + raise ValueError("aligned integer/string query IDs required") + starts = np.r_[0, np.flatnonzero(q[1:] != q[:-1]) + 1] + ids = q[starts] + if len(np.unique(ids)) != len(ids): + raise ValueError("queries must be contiguous, not fragmented") + sizes = np.diff(np.r_[starts, rows]) + w = np.ones(len(ids)) if weight is None else np.asarray(weight, dtype=float) + if w.shape != (len(ids),) or not np.isfinite(w).all() or np.any(w < 0) or w.sum() <= 0: + raise ValueError("one nonnegative weight per query with positive total required") + return ids, sizes, w + + +def validate(arrays, patience): + if "weight_train" in arrays or "weight_validation" in arrays: + raise ValueError("ranking rejects row weights; supply explicit query weights") + train = groups(arrays["query_train"], len(arrays["x_train"]), arrays.get("query_weight_train")) + valid = groups( + arrays["query_validation"], + len(arrays["x_validation"]), + arrays.get("query_weight_validation"), + ) + if train[0].dtype.kind != valid[0].dtype.kind or np.intersect1d(train[0], valid[0]).size: + raise ValueError("ranking query types differ or train/validation queries overlap") + for key in ["y_train"] + (["y_validation"] if patience is not None else []): + y = arrays.get(key) + if y is None or y.ndim != 1 or not np.isin(y, [0, 1, 2, 3, 4]).all(): + raise ValueError("ranking requires relevance 0..4") + return train, valid diff --git a/benchmarks/v1/ranking_smoke.py b/benchmarks/v1/ranking_smoke.py new file mode 100644 index 0000000..9a9d2ba --- /dev/null +++ b/benchmarks/v1/ranking_smoke.py @@ -0,0 +1,80 @@ +"""Weighted query-aware ranking fit, validation stopping and score reload.""" + +import hashlib +import importlib.metadata +import json +import pickle +import subprocess +from pathlib import Path + +import numpy as np + +from benchmarks.v1.baseline_worker import fit, predict_saved +from benchmarks.v1.quality import metrics + +if __name__ == "__main__": + rng = np.random.default_rng(73) + x = rng.normal(size=(96, 4)) + y = np.tile(np.arange(4), 24) + x[:, 0] = y + rng.normal(size=96) * 0.5 + arrays = dict( + x_train=x[:64], + y_train=y[:64], + x_validation=x[64:], + y_validation=y[64:], + validation_row_ids=np.arange(64, 96), + query_train=np.repeat(np.arange(8), 8), + query_validation=np.repeat(np.arange(8, 12), 8), + query_weight_train=np.linspace(0.5, 2, 8), + query_weight_validation=np.linspace(0.5, 2, 4), + ) + results = [] + for library, cfg in dict( + xgboost=dict(max_depth=2, reg_lambda=1.0), + lightgbm=dict(num_leaves=4, lambda_l2=1.0), + catboost=dict(depth=2, l2_leaf_reg=1.0), + ).items(): + job = dict( + application="A4", + library=library, + seed=73, + threads=2, + device="cpu", + early_stopping_rounds=3, + config=dict(rounds=16, learning_rate=0.1, **cfg), + ) + prediction, saved = fit(job, arrays) + replay = predict_saved(pickle.loads(pickle.dumps(saved)), x[64:]) + np.testing.assert_allclose(prediction, replay, rtol=1e-7, atol=1e-8) + stop = saved["stopping"][0] + native = stop["history"]["validation"] + metric_name = next(k for k in native if k.lower().startswith("ndcg")) + vals = native[metric_name] + assert stop["selected_rounds"] == int(np.argmax(vals)) + 1 + scores = metrics( + "A4", y[64:], prediction, query=arrays["query_validation"], row_ids=np.arange(64, 96) + ) + results.append( + dict( + library=library, + status="pass", + stopping=saved["stopping"], + metrics=scores, + reload_max_abs_error=float(np.max(np.abs(prediction - replay))), + ) + ) + result = dict( + scope="synthetic CPU ranking adapter only; no real A4 quality or CUDA acceptance", + cells=results, + seed=73, + threads=2, + source_sha=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + packages={d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + source_hashes={ + n: hashlib.sha256(Path(__file__).with_name(n).read_bytes()).hexdigest() + for n in ["ranking_smoke.py", "ranking.py", "baseline_worker.py", "quality.py"] + }, + ) + Path("benchmarks/v1/evidence/ranking-cpu.json").write_text(json.dumps(result, indent=2) + "\n") + print(results) diff --git a/benchmarks/v1/real_data.py b/benchmarks/v1/real_data.py new file mode 100644 index 0000000..f3deb47 --- /dev/null +++ b/benchmarks/v1/real_data.py @@ -0,0 +1,298 @@ +"""Verified real-data parsing and deterministic partitions; no model fitting.""" + +import argparse +import csv +import gzip +import hashlib +import io +import json +import platform +import subprocess +import sys +import zipfile +from pathlib import Path + +import numpy as np + +SOURCES = Path(__file__).parent / "datasets/sources.json" + + +def sha(data): + return hashlib.sha256(data).hexdigest() + + +def array_hash(value): + a = np.asarray(value) + if a.dtype.kind == "O": + raise ValueError("object arrays have no portable byte identity") + if a.dtype.kind in "f" and not np.isfinite(a).all(): + raise ValueError("nonfinite data") + a = np.ascontiguousarray(a.astype(a.dtype.newbyteorder("<"))) + return sha( + json.dumps([a.dtype.str, list(a.shape)], separators=(",", ":")).encode() + + b"\n" + + a.tobytes() + ) + + +def group_splits(groups, seed): + a = np.asarray(groups) + if a.ndim != 1 or not len(a) or a.dtype.kind not in "iuUSf": + raise ValueError("one-dimensional group identifiers required") + if a.dtype.kind == "f" and not np.isfinite(a).all(): + raise ValueError("nonfinite group") + if type(seed) is not int or seed < 0: + raise ValueError("nonnegative integer seed required") + ids, inverse = np.unique(a, return_inverse=True) + if len(ids) < 5: + raise ValueError("at least five groups required") + order = np.random.default_rng(seed).permutation(len(ids)) + cuts = np.split(order, [len(ids) * 3 // 5, len(ids) * 4 // 5]) + return tuple(np.flatnonzero(np.isin(inverse, part)) for part in cuts) + + +def stratified_splits(labels, seed): + a = np.asarray(labels) + if a.ndim != 1 or a.dtype.kind not in "iu" or type(seed) is not int or seed < 0: + raise ValueError("integer labels and nonnegative seed required") + classes = np.unique(a) + if len(classes) < 2: + raise ValueError("at least two classes required") + rng = np.random.default_rng(seed) + splits = [[], [], []] + for cls in classes: + rows = rng.permutation(np.flatnonzero(a == cls)) + if len(rows) < 5: + raise ValueError("at least five rows per class required") + for out, part in zip( + splits, np.split(rows, [len(rows) * 3 // 5, len(rows) * 4 // 5]), strict=True + ): + out.extend(part) + return tuple(np.sort(np.asarray(s, dtype=" 0 + valid = (joined >= 0) & positive + counts = np.bincount(joined[valid], minlength=len(f)) + totals = np.bincount(joined[valid], weights=s[valid, 1], minlength=len(f)) + contradictory = ((f[:, 1] == 0) & (counts > 0)) | ((f[:, 1] > 0) & (counts == 0)) + data = { + "x": f[:, 3:], + "group": ids, + "y": f[:, 1], + "exposure": f[:, 2], + "paid_count": counts, + "paid_total": totals, + "aggregate_eligible": ~contradictory, + } + for label, j in [("Area", 3), ("VehBrand", 8), ("VehGas", 9), ("Region", 11)]: + data["category_" + label] = np.array([r[j] for r in freq]) + # Policy-row mapping gives claims the exact same entity partitions as frequency. + data["severity_policy_row"] = joined[valid] + data["severity_y"] = s[valid, 1] + audit = { + "frequency_rows": len(f), + "severity_rows": len(s), + "nonpositive_claims": int((~positive).sum()), + "orphan_claims": int((joined < 0).sum()), + "retained_claims": int(valid.sum()), + "positive_count_without_payment": int(((f[:, 1] > 0) & (counts == 0)).sum()), + "zero_count_with_payment": int(((f[:, 1] == 0) & (counts > 0)).sum()), + "aggregate_retained": int((~contradictory).sum()), + } + return data, audit + + +def summarize(data, split, name): + rows = np.asarray(split, dtype=" hi)).sum() + ) + folds.append(fold) + return { + "schema": "openboost-real-data-v1", + "dataset": name, + "split_rule": rule + + "; sorted unique groups/classes; PCG64(seed); floor 60/80 percent boundaries; sorted source rows", + "arrays": {k: {"shape": list(v.shape), "sha256": array_hash(v)} for k, v in data.items()}, + "audit": audit, + "folds": folds, + "sources_sha256": sha(SOURCES.read_bytes()), + "adapter_sha256": sha(Path(__file__).read_bytes()), + "hash_format": "SHA256(JSON [little-endian dtype,shape] compact + newline + contiguous C-order bytes)", + "status": "data preparation only; no model quality claim", + } + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("name", choices=["covertype", "parkinsons", "concrete", "veteran", "insurance"]) + p.add_argument("directory", type=Path) + p.add_argument("--verify", type=Path) + args = p.parse_args() + record = describe(args.directory, args.name) + if args.verify: + frozen = json.loads(args.verify.read_text()) + frozen.pop("provenance") + if record != frozen: + raise ValueError("frozen data mismatch") + record["provenance"] = { + "git_sha": subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + "dirty": bool(subprocess.check_output(["git", "status", "--porcelain"])), + "argv": [sys.executable, "-m", "benchmarks.v1.real_data", *sys.argv[1:]], + "python": platform.python_version(), + "numpy": np.__version__, + "os": platform.platform(), + } + print(json.dumps(record, indent=2, sort_keys=True, allow_nan=False)) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/requirements-cpu.in b/benchmarks/v1/requirements-cpu.in new file mode 100644 index 0000000..2775c34 --- /dev/null +++ b/benchmarks/v1/requirements-cpu.in @@ -0,0 +1,8 @@ +numpy==2.3.5 +scipy==1.16.3 +scikit-learn==1.8.0 +xgboost==3.4.1 +lightgbm==4.7.0 +catboost==1.2.10 +ngboost==0.5.11 +xlrd==2.0.2 diff --git a/benchmarks/v1/requirements-cpu.txt b/benchmarks/v1/requirements-cpu.txt new file mode 100644 index 0000000..d30c40e --- /dev/null +++ b/benchmarks/v1/requirements-cpu.txt @@ -0,0 +1,925 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile benchmarks/v1/requirements-cpu.in --python-version 3.12 --generate-hashes -o benchmarks/v1/requirements-cpu.txt +autograd==1.9.1 \ + --hash=sha256:7818c5c69ddf9efb7da74097bd741a4c7920133f41966f68537223a15ef798bc \ + --hash=sha256:b788bae3fa010cbffb4cfb7b8ba2a3f0daa6072a8506da6164c779fe9cf3e05a + # via + # autograd-gamma + # lifelines +autograd-gamma==0.5.0 \ + --hash=sha256:f27abb7b8bb9cffc8badcbf59f3fe44a9db39e124ecacf1992b6d952934ac9c4 + # via lifelines +catboost==1.2.10 \ + --hash=sha256:19de3cb267be3ddb8fd667a87f9e7d3c9ee31783c61ea9e6e6f036f666bddcc3 \ + --hash=sha256:21deaef3f6f49e70b320ec48f4741133287e888297c42af8bd677ac636e8fc64 \ + --hash=sha256:22aa943cc6f7839ca5d3d66d4f8763d8c799fcf43d64d209e14e2e66016fdae6 \ + --hash=sha256:25c9b0dd9afb464efe7ccabf7567241aa566f70e7f77893218cb9fa21663e5d5 \ + --hash=sha256:26ae6d423acaf0e9d8160f2477a990431057ed04522d993c2f42dac62743b4f7 \ + --hash=sha256:2a19c1a9e92c76fb5dc75cf6a5b0d03127f3a36359e1e02e5d139e27581e2d57 \ + --hash=sha256:39234b3692b6c9002b4a2ac529025fc210dd72feb9b621b27d17c65b7d3e9f92 \ + --hash=sha256:3efc5e4d414b7c13bff6dd0d6c938cf09bb1445097283c7790e54b8ee461820b \ + --hash=sha256:41bbe16cab0695978c325a20fa300f92831ed78e9cc8c5fe8047538b4055e98e \ + --hash=sha256:42c1b6c7ae5c18cdbe00c8b9493987cc13338fe328baaf1a0b98ddaf58db96a2 \ + --hash=sha256:4debc33c278e431681d47d90818c15ec58407c8ea028b3060953dd29a6246946 \ + --hash=sha256:5319c7f9a7764d7dba04c218fd28383b7267553f83232e8ce8737d6b8d38534d \ + --hash=sha256:56c2c0ec0c16874b83d39f892b7f8a026bbd7404d59b23a34ce53f6b4b87b26a \ + --hash=sha256:5819a880af6b314f4980e6c26ad0f7552eafcf247d521bc884fe726347fdd87d \ + --hash=sha256:59aa166f075f0a5ea57b0ba46e5060bd6a22e849e91e4142f16c2df11295b184 \ + --hash=sha256:5ede858e634d6d0f521bf6dd6fad9374f23d37049ee48e0779ccd2a372632cb1 \ + --hash=sha256:5ffe85f53092219cf65c73c2946426a289ef6f62c119c2bfda52815250d9bcef \ + --hash=sha256:6b8a7ef11d7a89fc547760cfafeee895011a4b92cc1f60d00235ef80a71158ed \ + --hash=sha256:7b8cc4ea3a6ac4a8d05f3a79c8ee5454360a0a710fa12444963865ad3f0ddfec \ + --hash=sha256:951c5bdf27b8edb6ca624f41134888c666ae68275488803d3c91ce83e154f0c5 \ + --hash=sha256:a1eea0b556d1c154907a6896eb865e1bb39c9b974e0765d879a41fbf87d4639d \ + --hash=sha256:ab2e84237308d62bae236b1ecba2e3867697f96bdbaf0ca68dafc2c886946406 \ + --hash=sha256:b27115d5b443048f710001c8ac666892dfe03498492310b00466203c91cc30a5 \ + --hash=sha256:b28f763776e62f50da90dddf73b36399583295032667a7e46fc5c1f2593eb80f \ + --hash=sha256:bad9a70890cdc591080a908d54a3cd70002ab1e48b2017adff84726da0b3e16d \ + --hash=sha256:bd3d3b344894f61b5f70124658f302148bb9a51c41d0d5b6c453a72e9dfefc49 \ + --hash=sha256:c20dbca7fb73458e7f017faf091b91faf3f106e113d6019e8ecb99c452169426 \ + --hash=sha256:cf54c216f6b3b102e06a5fc42deeb7a2497d622e6bc2e222f586e7e357a942f1 \ + --hash=sha256:fc040b85d06588bc0d22bc4941208f43b4a56fccd4ff78b738ee823956b89370 + # via -r benchmarks/v1/requirements-cpu.in +cloudpickle==3.1.2 \ + --hash=sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414 \ + --hash=sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a + # via joblib +contourpy==1.3.3 \ + --hash=sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69 \ + --hash=sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc \ + --hash=sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880 \ + --hash=sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a \ + --hash=sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8 \ + --hash=sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc \ + --hash=sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470 \ + --hash=sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5 \ + --hash=sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263 \ + --hash=sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b \ + --hash=sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5 \ + --hash=sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381 \ + --hash=sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3 \ + --hash=sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4 \ + --hash=sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e \ + --hash=sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f \ + --hash=sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772 \ + --hash=sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286 \ + --hash=sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42 \ + --hash=sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301 \ + --hash=sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77 \ + --hash=sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7 \ + --hash=sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411 \ + --hash=sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1 \ + --hash=sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9 \ + --hash=sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a \ + --hash=sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b \ + --hash=sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db \ + --hash=sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6 \ + --hash=sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620 \ + --hash=sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989 \ + --hash=sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea \ + --hash=sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67 \ + --hash=sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5 \ + --hash=sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d \ + --hash=sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36 \ + --hash=sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99 \ + --hash=sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1 \ + --hash=sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e \ + --hash=sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b \ + --hash=sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8 \ + --hash=sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d \ + --hash=sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7 \ + --hash=sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7 \ + --hash=sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339 \ + --hash=sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1 \ + --hash=sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659 \ + --hash=sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4 \ + --hash=sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f \ + --hash=sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20 \ + --hash=sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36 \ + --hash=sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb \ + --hash=sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d \ + --hash=sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8 \ + --hash=sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0 \ + --hash=sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b \ + --hash=sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7 \ + --hash=sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe \ + --hash=sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77 \ + --hash=sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497 \ + --hash=sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd \ + --hash=sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1 \ + --hash=sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216 \ + --hash=sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13 \ + --hash=sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae \ + --hash=sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae \ + --hash=sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77 \ + --hash=sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3 \ + --hash=sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f \ + --hash=sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff \ + --hash=sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9 \ + --hash=sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a + # via matplotlib +cycler==0.12.1 \ + --hash=sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 \ + --hash=sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c + # via matplotlib +fonttools==4.64.0 \ + --hash=sha256:043f6c572bf236f2a76e762c25f841daea11e8fc03e78088d7be66e0c5b4e4c0 \ + --hash=sha256:06b6409b868494556a831ae33b2d9a090476c37516b38d70f45a9720b460d423 \ + --hash=sha256:08f172961e11f4eb4f80f2f20049e09b0ea8e044fa6d456fed8346eb8588f360 \ + --hash=sha256:09657817b75575822bcd6098ef0ebf0386f34430839ee53109e70fd40a7f6539 \ + --hash=sha256:1c3661324f3f0fa4539a32288a3e0711a5f3ccf020036e760bb558ae9811a16f \ + --hash=sha256:1e4e84b47839d35be24dbf476845a34f2ccf99707b66df125c1c414d3e86d25d \ + --hash=sha256:236e59bc7e2a63557a4d7b013f9cb9e28d9aebc45bc09f85e545e6bf091db626 \ + --hash=sha256:2524a26f8fdb9051b0d778d052f5d238285ca9f91a7dc004514c7d6cf38d35f4 \ + --hash=sha256:2730946ca8f12c356bd98eb9b2b095c8e761ed05bed5afb0d5b380cebe4f6370 \ + --hash=sha256:2c42237b7e8c6813643e57d3efed3be094d4c06339dc2166b626e2cc5c12ee93 \ + --hash=sha256:3200180abc69639483cf54a17cca2e13c31ede5f665979ea0a9c829d093f372f \ + --hash=sha256:398b14f89ca950b288bd290875f07e4e10685644fa4ac668546fb107b1ada4d4 \ + --hash=sha256:45e3ecc3888f1637094fd75cd8fc727f3a4b06d1ddf89181126c071e244fd2a5 \ + --hash=sha256:4691a122b8c1d0d82d6e7510ce59d5c42146518240274b53e912e255573924f7 \ + --hash=sha256:498f02ea92c9ca18c0f9c581ea93184a9d56c25b0af14189b0767adaf34235d8 \ + --hash=sha256:4a05783ff54ce4c7a28f18e5772efdf63c219374bd9ffc55452182e1cef8be60 \ + --hash=sha256:507c553cdb5abe2e951b5368423849fe29911a828c2135319c3e500e3bf25b32 \ + --hash=sha256:50e52b6f479ddb1fe32423c2ec860811f36584cf6eabf279fb9a4f98b859a8b4 \ + --hash=sha256:53eee22af5b5a305c1ee2652955ed46b148e881456fcec1e7f0eb27f642f6bb4 \ + --hash=sha256:5af87d1a6d247d7467ee082ae977a5443b2c45f8cd4d59375b6daa38d523c2de \ + --hash=sha256:5b90ad6637237b636d15c9ae8b7c4a7a1c194f33def378677e468c13fd4542f8 \ + --hash=sha256:5bfdaada437e7730c17d366bd7bb8c4a16639963ddbfc1b2f302a68a17a290e7 \ + --hash=sha256:66a83f93579fb3493e458c4449d1d566a7b2a1c7b19915cd0fa3c9b8b5a8540b \ + --hash=sha256:6786bed88581e19bc4f28ea7a64ad531e8f54acf50327fddca942688824a60bd \ + --hash=sha256:6946c033a144086d5b98c976b72f476b70c93fbbedf914eee0e886f073a4e9fa \ + --hash=sha256:6eae4376adb104c2acfa76fd9ea0cb12b572ca1d70eceac709871f638ff76e93 \ + --hash=sha256:6f1ce9ef9a1b13098efdc2e43a2ed96d9851bbde7b31c652a87552c4efe9b422 \ + --hash=sha256:70fd99e5a09fb77f14b29d70879a4fce9529b2d2948b14c96708e0a61e001b98 \ + --hash=sha256:730eed859508cb7b0775ebe6bb39f18901f168eb989d8ee23a4fe082700e1e3f \ + --hash=sha256:769fb64412ca237547ca73f111a64252d9e32c9d938bed51ed537bc9146a8f54 \ + --hash=sha256:7d7995b906666037d7114c20a5566a372902747452af7d5bd4cd6bca8f1a2550 \ + --hash=sha256:801fd04899d72eab34f02ab78d0451525621b3bd589da9d2d480dfffe951b643 \ + --hash=sha256:8252f20108e557532f91d7d6dd9af87c16ed6fa930f65516aa480fa2cfed3363 \ + --hash=sha256:83cc48d1411d2ff388dab99973dca81172cc9ceae9c9799da9548d494cfb38cb \ + --hash=sha256:89356c0793b474af7e49ec90d39fb2363e2341516a90460e38231df5ebe8acd5 \ + --hash=sha256:8dd18fdff0ac9759b8d67a714730abee07b2312e3656c20ba5affb0107094762 \ + --hash=sha256:917fd520bb60809d83c14d43cfe48d5ad2516abaf2c073d65a431800dade2d29 \ + --hash=sha256:9443eefff58aad558608f352092e1be6d278980e8c3b4e8621fcbfda97818500 \ + --hash=sha256:9ecb2b206b5b2386f6968721a0770226b66bdd54adc4279bfff3ddf62873eed8 \ + --hash=sha256:a0afa8bac675445dc0e2ba2891ecbedd9be89cb437afa94c823e0290cc2c4bc5 \ + --hash=sha256:a3238a693e806a3158375c6403b8f6f71d86eb9c149b60c97f26dfd560c98ac8 \ + --hash=sha256:a515f664cad988f2295056833a59f62220bc3e46afdaffe389a29060f6712355 \ + --hash=sha256:a8c631303bb1fd7be3067c47536a30ff1fcb4846d6008c112bc52a03f7cd6965 \ + --hash=sha256:b2763e452b025ee8e990f0462e76052de9bb094ebc21d296f62c6dfe958886b4 \ + --hash=sha256:b4a7af455ffed980925bc0ebf5b8d6239e6c3e797d9d755b6db192fb3080d614 \ + --hash=sha256:be084d19a3ac0c8b2aba696680642d703118d3b1f18cf83f5b7dbaf0ffc62ab6 \ + --hash=sha256:c3c1fb656063a2f762db5378ea8d38ad5f7836b4f3fb8c4652270ded43df2935 \ + --hash=sha256:c60be0aed97a32c6ba8cee21f0d0477136e495451bd97910f589ac892db120d4 \ + --hash=sha256:cf67f96dc0bfe9607f5f2b734cedfbe2f6f995231adee4ccefa12872044d452d \ + --hash=sha256:d16102cbcd4615b09c64e6022733faccc93200785f1ab0d4493afb8b0261edde \ + --hash=sha256:d30c966bea2deffa19c738c81776f7182da5ccabd97e666bae4f3d6ba87341d9 \ + --hash=sha256:d652592c71683941b768306fa1c7c6ce1bb9b072505043feafe86305d71030b7 \ + --hash=sha256:da4c9bdeaf6b06c12d13d0addfc8ef15aa9695d26574a6dc10751258bef72f30 \ + --hash=sha256:dac25768be4c03a990c359f408cb7e8958ed0e93061e495b3642ce7909761205 \ + --hash=sha256:dc96150f99e05a317cb1f042b92c4cf8bc93cdb1f9f85717322e202ecdf2e505 \ + --hash=sha256:de8acaa5f4160f537a3cf41b031171d51004b9f4aebfa6c194f18dffa9533d03 \ + --hash=sha256:e412767d1c9765cf1b82f7b00f1686c6ca5809ebb77af363b3f9f2325a465c01 \ + --hash=sha256:e4812f71c39d77ec5041348dafa400532adf7bf8f1fffa9aa6495fce5876d7b8 \ + --hash=sha256:e63b63b8b5fdb8e29318dff2b15c5f852be46e972775b466f75b848f6eed4502 \ + --hash=sha256:e662f874ab2c7da9861584db44a13573e0936df087215f63013138f6e5eba083 \ + --hash=sha256:e7b34209eef39462563c05ea9dcf51c272a2ded56f5753da925e66bca3baa484 \ + --hash=sha256:ecb2e59a7bc692fee64dda6010deb66222335693b30046f15cccf81233aa715f \ + --hash=sha256:f521d79d6acda4923b264805541696f452079db0952a5bb96f9ff742f50629ec \ + --hash=sha256:f8669ce37851b597d3435b91fefa51139e58d506ca449ca0e5bb68c63b8b6d2b \ + --hash=sha256:fa75c7970bc6bca340cc6e20f20f069201bfcb50094c31a536fd99724d1d01ca \ + --hash=sha256:ff7aff4637fbf71394df139c63ccfe08a47aa4252d2f91224ddb3335c716c925 + # via matplotlib +formulaic==1.2.2 \ + --hash=sha256:0f84ff49e3fc9dc0e68ab08a0a9427874021aa6c558e66b44dc634a35739b09b \ + --hash=sha256:c99e8f11ff7d327eaecaf63855ca69b7fa0da100ad6c0041ef80912fbac667e6 + # via lifelines +graphviz==0.21 \ + --hash=sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78 \ + --hash=sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42 + # via catboost +interface-meta==2.0.1 \ + --hash=sha256:902bd9a95a12f195f15753a1080075d4eca7a2cb934fac7ac03c9e362b50796a \ + --hash=sha256:f38016bef9a4429b6d0792d809be7b65e9781820c674bf7f463999086b6e6323 + # via formulaic +joblib==1.6.0 \ + --hash=sha256:2ccc96785b12046c08fd6d55839c12857831b54a3c1673ffadd2f04bfc4eda03 \ + --hash=sha256:3dbbf9f6e4b592a2357b854608e980fe6390d131d7a82f011a377ef2ebef7aba + # via scikit-learn +kiwisolver==1.5.1 \ + --hash=sha256:007a5553dfc4f4e8d184f588a0200e2cd4b63a59cc8796df3c39909e679dc7a0 \ + --hash=sha256:0324cd2567259b7a095f6cf18a52b0ffc6f3de9e69528ff1bc0e7a37bd43ff1a \ + --hash=sha256:0627b9bceb9c3cdcf12b8a18655eedfed2692b038df27423383c120d0b7dc2d6 \ + --hash=sha256:06a6917674de9e0fe3f66f5430787f59a9f2ddb64af9b714eaec547e29ef5c19 \ + --hash=sha256:072bdb15a3c19a5b5dbc8f8fb1f4e1884bf4f3507eeb4cc6334401274d37a5c0 \ + --hash=sha256:0a4faea5c6db201c6a21391d2ac926ea97acf7dacdbc3c417189e1adb1a00837 \ + --hash=sha256:0ba9527afc80ae3d7814ed98b6572d02bf85eaf48065678342c5f0c6dab7a8c7 \ + --hash=sha256:0d8924877ce22e17326a99a418c3c82037da078df3c6a260b13eca677444e6e7 \ + --hash=sha256:0ebdef3eae5336568147c39a55be6a2036ffde53faa9ca2d978989ae7c2da12c \ + --hash=sha256:1209042a623ddfda5497e4066c7b77651dde8e1d3a9dd97599dc7e97f3b9b78c \ + --hash=sha256:16895f553ee6620a827d2da56b871f835fb70b9216cca5d188e885caf6e3bd23 \ + --hash=sha256:17851e5dad4484be0cbccbde3b15331deae036de9aebd45eed964487802b172f \ + --hash=sha256:1798e83840c3f627246104c4d8a9639c60fa068adf9ce92b61791781fa8a68c1 \ + --hash=sha256:18170a77ddfecf40ec60d0928268dc95880c881864e015a8f34094ed18b9b9ad \ + --hash=sha256:186884a58486651e3c217b6acea0a53eaa9498fdd472057c46f2f0fb5c25aad5 \ + --hash=sha256:18a0cfb124546a4c2e6087c5f3029c7f44b37c85b142e0ced71f73a7599ac208 \ + --hash=sha256:1983f0974a750a6f6556f368ba11105d1d8369c735b944747c9f12ae5aea7aae \ + --hash=sha256:1a7587dc335f2c0f5bd577fd0540bd16c66006bdb60f759a1059f025e6c4f071 \ + --hash=sha256:1acc7e5b7ef05e9da8bb70cd6c7c4513090213d2e1ad9720f599f0bf6c52aec5 \ + --hash=sha256:1d852545c4d0e35a72728d072cbaa59e2fa7dd84bdf01e068d670dd0ceb58eb6 \ + --hash=sha256:1ed0f5e49d0ceff8b72190824d9e59c062fbbc02c231b853112c78474b3f5ec2 \ + --hash=sha256:1fff05e239575b1481b6ed1a782f6fad616efbf1f0b1f44e6e85c4dfe426e483 \ + --hash=sha256:21e46b23a2da695c364124817bc01d970effd5483147f8d66a6a7167e3f6b851 \ + --hash=sha256:22d5e5aaad6be121f2515765e3b1c444352cb8eb4c86510801db8f2e50757316 \ + --hash=sha256:2551cf9917af48ee7c4b29cc82320489508cf96fd26a51f6fc124de661cd44c7 \ + --hash=sha256:255605693a483db7bd5c79f60437f7bf658f7f520d61aa42722e32257c941951 \ + --hash=sha256:26e8268480be5061d509e29669d59103c067a26377a56491630ece11762e3858 \ + --hash=sha256:27add358abe374ebaa3b8763ef380bc99051b5a4b18d94878366a9e4f59efef0 \ + --hash=sha256:2ae70bc59790d2af72a3f76f24b272403e135070340281108b447cb77ea70819 \ + --hash=sha256:2e10ae1bba1899188b33557c10d73affcc12033edd18adddb57d209039976a4c \ + --hash=sha256:3221f78211074f561c44ca42eac0619828171bec15a2c4cf6f7747d07df76e8e \ + --hash=sha256:34633ecf50d16187ab8e5528b7a2530f2feb4e23f300db4672538b51cfc5cd38 \ + --hash=sha256:34ec467940442c9943016fb2d4c81d1ba84351eeca2f1a78f8bc87f1ba0d414c \ + --hash=sha256:37f801b5d7cc0e5a548921308e059fd2b057bb42972b591cfa3049f95423c4ed \ + --hash=sha256:38f6e0deb4d0a4615efe0c4efc5990b06ae450ab50a0b321c0b078b6d238c083 \ + --hash=sha256:3c24cd69455e1b00ddf770c13b6e2c33e07d6dc3f2d34add0bf9277c5c6bbd46 \ + --hash=sha256:3cc210010fd2f438a3ed430b45f1b501fd13a8618bf984dc2c5ce5b69b78752e \ + --hash=sha256:3fa5855898f6d3d01b72ccd48a2d65cbdee301251603fefe34e2025bddba219c \ + --hash=sha256:416ba7ff9f233b7036689bb5a3783537e838ad483f63558d2a800f75afe738b1 \ + --hash=sha256:431dc224a1a92a5c8f582d96e505196a3b5997a7271076678da2dfde67b77e9a \ + --hash=sha256:43844c1a7ad6d723d5b5b4c4fc7f5bd399c40e288120d16257c7c9e8765c6e85 \ + --hash=sha256:44b8faef94f1857e77fa0238f3390ff1ac51d2ea20a487e2e452a59fd2b5f5ca \ + --hash=sha256:470d420f98d368d6f010633a20659b544c5fdfa5329e6b70219f2ef08fd4a7ef \ + --hash=sha256:482676e5bd48d70ac99d9fc78863469845421e01184fa83f1f9366dc49f7e974 \ + --hash=sha256:4d4ca09bf13cff792b1884f64b98ee6c2467930d632233be25c56b442d99f10e \ + --hash=sha256:5025e36fb4fb275cef0a4e30dbb11cb4ae61d1c83deb90189cb5d7e4cafd6b55 \ + --hash=sha256:509735237ae0d849e8a843551d423d2500d2e0a9ac1611a145658b29c0fb9f85 \ + --hash=sha256:534f02c1abb31ed6dbd3515545285c330b2f12d00fdb1fdb71658b9ca5a13a6a \ + --hash=sha256:5978c3340f16a35c30f8ab2fa7bcf559973c55f1a5ef6970e1f621acf3c4db13 \ + --hash=sha256:5b973887ff782cfd6b67c9904ad8ca542e0bc5e4961503408b423b5a688b4d38 \ + --hash=sha256:5c490db2168a508088f59140dd392556a54b8bd1048fc6383c8baff13c359673 \ + --hash=sha256:5d142e352eb13facc7dd047489aebdff6ba78576c239f1ea04931979caaf0567 \ + --hash=sha256:5daa1f19e097050b9c4d9a78fcc9263cb96c9dfae08037ddc1b7c4ad1889f2a2 \ + --hash=sha256:61e9a64c7635095a6bfe483e2ff055d437c59bd45f3617a228b37277f0185d62 \ + --hash=sha256:63fb7294b768f444eb4b068965f2662f28c2fd4161e23bd60fcf3ff27b74c046 \ + --hash=sha256:685929988b208a911f1285e2f8ed54210b0d681a3dc0f03e00d599d291986e7e \ + --hash=sha256:6a797a1cefc8b9c93170db580337e1fe3d011ad18b1299943231279406342048 \ + --hash=sha256:6b92f60017dda7d877fdc546438b5e28f31c523264f49cf5a48c1d0ce1a0dfbc \ + --hash=sha256:70ed9a45c7484d2b30cdacf60d220f494a1763b9fec1ad03285c6553fa0889f2 \ + --hash=sha256:719a35fa1156db3640555f95ebb94f60a444e64d1c69626b0edef5df78eba225 \ + --hash=sha256:74ad5c3dad54a4641b4c28cd15ded70899d04459c6c7aeacafea716be97cce6d \ + --hash=sha256:74ea337e0ec3f6f342a36a4f1b5cd94dd9affddcd28ba9aae2905af932ee8c6b \ + --hash=sha256:75d9b1cf8258462dbdc1eeda718c96ea7f079324c09067f6daabfcf37712b7fe \ + --hash=sha256:77a4c8187a5948d7f8795adb765a3c7b553d07d86d88e43038fc32fc1fb9a3f3 \ + --hash=sha256:7824b5e8bdbf0bccb4ccd37bbb115849a1dc45437fb4de8351385ed07c437ee0 \ + --hash=sha256:7d38b0c279c3032e8c9cc013b405c6df8e1668dbf15465779aa7f15f61201812 \ + --hash=sha256:7e9c01d3dd7ceba4d1d436cc021d40d592466e40b9bc7f5d83dc4e98a5c9cd8c \ + --hash=sha256:7fd82debf43c6acd0a94359d232f6bb516ee13f269a7993736a9ac9f988bb5d9 \ + --hash=sha256:824c3d763a05ea9e9003610145186b0e9848c7584a5575c79bac5a8e7cd80bad \ + --hash=sha256:828f75af2b0080c8a972e75f649ab46af008e92c6104a57a759157200b835b75 \ + --hash=sha256:83f78128fa28705fa85d01c59771c72fe81c11bd0e6155edbb9f818983a7d761 \ + --hash=sha256:876bbfd276473d3daffe30e8c975df4ed9429967b41a6cb362dbb5155b6f13ad \ + --hash=sha256:886fc26012f0e8b5f69d1cfe6d711f6b11f194621539bf8e6bb1c25c5dc82724 \ + --hash=sha256:8a34616dc2521cc8dc1d7d081734da63539f021ac0450ce950908340c6e7aa2f \ + --hash=sha256:8a708a47ade1fe19e8371d5da076bac0dd4b0a5a7985ad6c637f7f7e361b6baa \ + --hash=sha256:8af9b142ad719ae3a911ebf616bc4b78b32bbab84d6a40d3ad2f129670509957 \ + --hash=sha256:8bf4df63592c2a66b4f8edc5df2544998c288aa02f96ce0acd880cd1de8c8127 \ + --hash=sha256:8de6f2a4ce7e7bd27d23dd94abf0ccafe0e0e5cc9c764b0577191f2c25f08f26 \ + --hash=sha256:8f8fddb8e323bd6eee4e54e69a39243beab22689070f4c66b472c4cc88bb89d8 \ + --hash=sha256:8fca690b00c4c48f6c2a547b0160ed511357093a4e4c9b47e0fadf3128066d89 \ + --hash=sha256:9506e892bcc3b409831d363c6f53e5985e1c8d1f6f6b0256d00358684ff85378 \ + --hash=sha256:958254518717542d02d0688d0d20cbf771da5e415e6f49543f92481c850a4540 \ + --hash=sha256:95a02752aa032eef4aed01cda6d9b687c669bd0396bf4519eef8bba22a286720 \ + --hash=sha256:96c30002424670b5e1e46495c2b8cbffef39cf77c1d79e76462029d50339785b \ + --hash=sha256:98b208a7cc42c803445ef551d6753cc42a5ea13e9cab1ee66cd8b9cb70195330 \ + --hash=sha256:9b3092d8992a1d69b7a59c3e39f35e1b9be327a17f68a7c35fc17329e337d6f2 \ + --hash=sha256:9e51c119992ea8820706871c30a4642ec76de20ae82f9b50b9a45517d8e9f810 \ + --hash=sha256:a5716a33bfabb2c6ce27b6cf03253467b3804f83e215f4d202685cf93c6c9874 \ + --hash=sha256:a5a00665d1a0e26763a7338d7e911d4598fbc1d50dd0d6b7919b7dc6c5d6569f \ + --hash=sha256:a5ca5aebae78a0bc13c1943af4af615d4966c5b650b05d5aa83b50e427196fee \ + --hash=sha256:a7b85b2cc6ea45e5f7e8c9a30bc9fabd47cda09106cbb4b967335c3e6c43b69d \ + --hash=sha256:a83ee7107df13abe42a54a6654670eef9bb39425cf2e27f65e0007465e1286ab \ + --hash=sha256:aa7d00b1700966d2917e54d278aba86897890ca9276dd8b76cf6446b6c181b92 \ + --hash=sha256:ab620eb663952455271ac37f9aaad86b73c969c02f11f53cea405b38e96a4300 \ + --hash=sha256:ad8b9671348d7c8716715652ae11f85ed0eb99e265a2df2ca490577d69860b2c \ + --hash=sha256:aefe930d113798330e9462f7874542977869c0613cba3262e2de3a8d5dee8f3a \ + --hash=sha256:b03af77d77e50edba2030fd5f7c352ff209314b09030a3cba7c14edf9a09a444 \ + --hash=sha256:b390aec180a7c054919c04898835e1c77bced23ea8383eb2c570213bf25d1a86 \ + --hash=sha256:b3d78f7bb2b9d9a30345be1474b9aaa8685430b54afb51ba3639b5c6c11e9ed6 \ + --hash=sha256:b5664603a253efd3a75716d793d1d3a6a82723b61dc6db767b2460bbbeec4c0f \ + --hash=sha256:b69602970994a2ed8bbfa78c2f0394a7435226c6040489702d9f0a0ad0c07052 \ + --hash=sha256:b6ae6a0328f0bc035741820fdeecdcd67bf4694eee03972e843663107122f450 \ + --hash=sha256:bad20d4c69c851c982a1e3606f4c293edfd5a87885786c50082412240c4b1ffd \ + --hash=sha256:bb7c99f0673c03017a3ee01e54a5c2617a05468b11eabe513b0080e063ed95b1 \ + --hash=sha256:bebb89489b279b2f5661bbbb2abcc87bcd4a46607bb4a5c966f04f1db6b8df9a \ + --hash=sha256:bfd1de989b3330420e29de39352f5c049905c9e3ee67233a50d550e3d652c148 \ + --hash=sha256:c2306e8bb53601979fcb3fa09cc65e031876d9ae01eff2fcbcd7a84ef94d5bc1 \ + --hash=sha256:c3a4e41e3096bf1f0f1b76e2ffd6d828d6547f574f702d59bdbef7acfa59db9c \ + --hash=sha256:c6834b92dd2428e2dd85ef3d85f723d3c12f20aaf43a2ddd4f944ca25d833408 \ + --hash=sha256:c90d3022d8a94778939cda8638c6c8da8fa757b8958dad7ec868ce29c87681b8 \ + --hash=sha256:ca307d6c259e5c98d3cb9ade55342b47a6839762caf2536f3d7b46ee660cc82e \ + --hash=sha256:ca7f6fe0f37ca978a1e5eb7a3a68e6413f417e78e838324947ffd420202b198b \ + --hash=sha256:cb6fae641357ed2f6e533c0d3c6504a4a5703621a50c89459e46051d56b61140 \ + --hash=sha256:cdaeeb6c350106df6bf9d873395973e5f066a9713200b72cd64f55d0a3eafab6 \ + --hash=sha256:cea20da04494e662b83c872683bf4ff2345206043d036315ed0e924b652e7294 \ + --hash=sha256:cea90547bfd93807e0013a004dc76552be44fad3bc1cc2b38610a9e889ed098f \ + --hash=sha256:d09037ca068d784ebc4aec290ef952ca27ac15dd9c0b5801a88c6e1096b83e6b \ + --hash=sha256:d27c2123977cb9269c30a49ba45f03a4323017ef693e19db4ec9dbe1299a3002 \ + --hash=sha256:d50de98e8d807dc31822fff96f50293163a62418eb65487a21b42713d72ed0b7 \ + --hash=sha256:d66a64dd5dec136040ec2ae94aa026a912ee60fdd45bc28d3db30037fd809e88 \ + --hash=sha256:d79308fa689fac89cbcfbd4dbfc80b5f95c54c5a7fd4d194be221f9d33d026e6 \ + --hash=sha256:da3275833be0edbaf4830fae08bae3dc7219f40ce0c37eaa6c25825957e06612 \ + --hash=sha256:dc1a26b8e53395a01c2c611e58602fa47461f136fba7cd5542e6db6d64be1839 \ + --hash=sha256:dc23390afe9f4ef9ac3bcc72a03a56eebbde03f4c571a32cb38f859cff9a6524 \ + --hash=sha256:e05c2f7925f1d88778e53cb44f14e0223204a3bdd09a41664750363acfb1f2ef \ + --hash=sha256:e12dfea7f5fc2a34a9080efbf79c4c44eb380ec5b9c6fea09407e08f0d1e941d \ + --hash=sha256:e4e4523d6f336708d732516e6cfca7796cf3d96c9474eb5aecf6165f2f1fefc3 \ + --hash=sha256:e4e49f7e1a4e7191bdf9dc67a974db714501b1fc52c24324103d06a86abd5c08 \ + --hash=sha256:e68e151428b5384f766cd25739bf77c7e4a3dc93b5ded7a12118d9fbfdf78ab6 \ + --hash=sha256:e8e4d953faaded9ec7ede36824e9814082d22d4c7b1eafbfa079ecba8cd0d076 \ + --hash=sha256:ee9df1f0d77b9c6e94f4ac0fec533fbddd5ea3a327807f18d7b069ae019ded80 \ + --hash=sha256:f0a887b6565bbfe80efde2b7f6e8890d7d9bbdb11bdb17028a3690c32fe0621f \ + --hash=sha256:f0f4a42db92d6ec7677ab9d12830a2a8ec145a9c6d15db2b593466bc875c78d7 \ + --hash=sha256:f1303ef2eec81262a4b708c3e858afe58d7c75ad91c1c05266eda7673369859a \ + --hash=sha256:f1d56ec54d257d05e0b50f5780d967540cd07beeaf9e5f645b26d50cce79f4d8 \ + --hash=sha256:f4167e87b397f273dc2356fcf1eaf50a6bac51e6105f45103ef7129c8efb0255 \ + --hash=sha256:f76fc85bd054c806960f917ec0f329e24e436f1712267d90588e4c39890caa63 \ + --hash=sha256:f942903fde7363d1d879057ec5de01310efda2597161784d752fa9953a01a71a \ + --hash=sha256:f9b1c4900736e489a812c529100de4b8fb617d4db075e931e213c57424b83d9b \ + --hash=sha256:fc271a6f0a2126958f4090e5507b9da5848927dae331f8f763bd4aa642b3d2cd \ + --hash=sha256:febcce10f2bcdbb80b4ea919238a6a4ac13dbc4c7cadbe8d5d75c3682f8b5404 + # via matplotlib +lifelines==0.30.0 \ + --hash=sha256:ac7c602c8aceced9770d3977817c9d99c250ed8cd86f2567fa0d23e4e8014bf9 \ + --hash=sha256:f7f6f6275fcb167fe0f5b1ef98f868993f9c074cb74b1dd6e92736efa854be18 + # via ngboost +lightgbm==4.7.0 \ + --hash=sha256:129535462686f274df179133643118c5c5c5667167fe6c3a28d955f0b3c8e868 \ + --hash=sha256:d23e922acd891e77212e4d0fbcee9ba973c96dee479491341d05ba595357ebb7 \ + --hash=sha256:d4529acec5c6fefe4768302a529707d0ead90f6a6f42df694b856212e09695b8 \ + --hash=sha256:dfc1cfe8e760387be1e7ba7a214688be21fdff96e4ed9749188f83e1877c2477 \ + --hash=sha256:f42d1e5b32b6f170e606d7c689c6165671da98d7bf37f1addec2623efc8740c9 \ + --hash=sha256:f8e20f682c9aabd000bcf4a7ed8aa6f473c1adfecccae34ec24e823d156f4af0 + # via -r benchmarks/v1/requirements-cpu.in +matplotlib==3.11.1 \ + --hash=sha256:0c1f44890d435c1b4ef52f701ad5828cb450ea97bcc83918fda6be74965d6cd2 \ + --hash=sha256:11664c551345553db92e61cae6cf1376f138f8c47cafdf13b64b18f3e3e9e464 \ + --hash=sha256:1524e2bdd48a93557aa47ddcfe9c225dfdd57d5a01a5c49128c20f0632980ee1 \ + --hash=sha256:191163532cdefcb1571ca38a6d7e6474baccde64495783e6ba47aa07ec4b9bbb \ + --hash=sha256:1ac697e591c11b6ad04679a73c2d2f9980fe9d9f0311fb414a2e329706343dfb \ + --hash=sha256:216fbb93a74add02ddb4cb38ef5348f59ac00b3e84567eaf16598772d40e150a \ + --hash=sha256:21a67b961a6d597bca54fae826cd20695ba4a6e4d05424a08da6e13e3176fd6b \ + --hash=sha256:2abdee5ffa2fe11b2d19f7a5c63b785fb7c28cc46c7bc1814156341d9d1a33e1 \ + --hash=sha256:30c492d4ba9448595b6fd8708c6725963f8148e25c0d8842948da5b05f0ee8d3 \ + --hash=sha256:3d3fd84082b1afbd9398466c81309e20045be20d48fe0fb18c43504d164cbbb2 \ + --hash=sha256:427258425f9a3fc4ed79a91f9e9b9aaf5a82cb6571e85dc14063cc6fbb993741 \ + --hash=sha256:480194afceca4df2f137c2721227d3cba67121fbf4397b69cee7f83714b0a58a \ + --hash=sha256:54d47b8ae8b579633a3902ca5b4ad6c1e132a5626d64447b2e22a66394e79987 \ + --hash=sha256:5af0dcda57d471440a7b5b623e70e0a61003518443d9098f211a96ecfbbc25be \ + --hash=sha256:5e1f8922ba31959cf6a9dfb51be64b7f7bc582801a3957dc0c2f3afcd3537adf \ + --hash=sha256:5e510088c27a89d53580a752f959146893563e63c330e161d159b0fee652af6f \ + --hash=sha256:6771b0cd7838c6a857a7209814158c0ad09bfef878db3033dd82d70ad101f191 \ + --hash=sha256:67e4c3cd578c65ebd81bdc09a1b6592ceafee6dfafe116dc85dfcb647b5bbb18 \ + --hash=sha256:68408341f2312836fbbdf6b3c78047f65b2d8752f5fd221c3e72d348f5b34f8b \ + --hash=sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30 \ + --hash=sha256:6be943cb68bc6660ead58c55b3aa6366cba2ef7feb06460fbcce32360376f19f \ + --hash=sha256:7389b77ed2ab0552f46d9a90b81b7b8e6dfcdc42adc36c37a0865799843e0e3e \ + --hash=sha256:7f33a781e12b1e53b278deb2f5373c2e55ec4f10727be3440c0cfb5cda9f944f \ + --hash=sha256:83235693abde86e5e0129998f80ee39fc7f58e6d56a88fafb28a9278833e9d5f \ + --hash=sha256:88a2a27dd9691ae448dfae4b26f59036be90c3c28757edd3553a29559d00859f \ + --hash=sha256:89b193b255f4f6f7948dbcee3691f4f341ab05d9a8874a67b45ddb4182922eda \ + --hash=sha256:8b14eb22961fe865efb0e4ff167e333e428908b00115a8d800ccb65ee108e481 \ + --hash=sha256:9601a1e90be21e4884c53b4f3dc3ee0544654946f9975258d691f1c2e2f119c6 \ + --hash=sha256:96f4bdeea33a8d15a071dbfe6d119451b1d719c733ac666d65357082901a9099 \ + --hash=sha256:9a076f4fc5cdc43fdf510f5981418d25c2db4973418d9f22d8bb3dc8045ada78 \ + --hash=sha256:9fdf1c818ab05d0e74002091ddaf414478a3a449ec9d51c8976d45be7e3a01e2 \ + --hash=sha256:ac104be2768ffdd8655db9e71b768cbb45f2b9aa7b450cf1595e8f65d3822319 \ + --hash=sha256:ae30c6109848ac0f9fa36c5d6270938487614c47ba31860bd5361266dabc5685 \ + --hash=sha256:aee55e9041211bf84302ab55ec3965df18dd90ae19f8b58332a7feaf208bfe83 \ + --hash=sha256:b0a19dcf73406d3746d25a5ed42d713604c9a3e024d129b102852b0d941cb9f3 \ + --hash=sha256:b4c78ceb2f11bcac7389d305cda17aeb1f4586a857854ab5780bd3dd8dbfc407 \ + --hash=sha256:b7cf158e7add54a8d51ac9b5a84abd6d4e13ed4951b4f25f1c5139f41c2addb2 \ + --hash=sha256:b937b9dba5f5f6c1e31c47abe2186c865c0914fd18f2ce0dfc39c9adcef5951d \ + --hash=sha256:ba8f811b8ddfac493734d6af0b2dff96919d0c28ca0d641858dab4262777c6ea \ + --hash=sha256:c52f7ad20ef476806ed212380b1d54d20310c8b86bdc2c9a68b51f0024a44472 \ + --hash=sha256:c90be0b73568da4f662afac580956a76e308437e641b4a45aa08925eeb67d95f \ + --hash=sha256:d2ace7273b9a5061a3b420918a16fae1f2dc5dfee1abcc13aba71b5d94b1820c \ + --hash=sha256:dadfe80797174e2984aae3be0b77594a3c72d2c0a40fbd4a0de48d2728caf3ae \ + --hash=sha256:e15ef41507f3d525f46154ac9e3ae785dacde9f20e593a25de8986267892ef74 \ + --hash=sha256:e4b9ac2f1f607ecda2af90a5232beee2af7582fce1cc30c4b6a1b012dc21ee99 \ + --hash=sha256:f2912f647f3fbe1ccf085f91e213936f9101bead81a5e670565b1f1b3712f4fb + # via + # catboost + # lifelines + # ngboost +mpmath==1.3.0 \ + --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ + --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c + # via sympy +narwhals==2.25.0 \ + --hash=sha256:1f0f403e8c7e4463cde9bfe78b12fdd809e3ae3dda6d9b2f802934fb9c7a6a8f \ + --hash=sha256:62c036c810662bf7820b7737077176313bc59350eeeefb808510f388c743e4b2 + # via + # formulaic + # lightgbm + # plotly +ngboost==0.5.11 \ + --hash=sha256:873326442f00632829a521209b1030e150e0ef0a9de09952e044f8f3b8839940 \ + --hash=sha256:c3683334ab6ad58d79bc50aa11d8f090f4d452010c73dc2f2304affc448b08fa + # via -r benchmarks/v1/requirements-cpu.in +numpy==2.3.5 \ + --hash=sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b \ + --hash=sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae \ + --hash=sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3 \ + --hash=sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0 \ + --hash=sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b \ + --hash=sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa \ + --hash=sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28 \ + --hash=sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e \ + --hash=sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017 \ + --hash=sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41 \ + --hash=sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e \ + --hash=sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63 \ + --hash=sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9 \ + --hash=sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8 \ + --hash=sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff \ + --hash=sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7 \ + --hash=sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139 \ + --hash=sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4 \ + --hash=sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748 \ + --hash=sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952 \ + --hash=sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd \ + --hash=sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b \ + --hash=sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce \ + --hash=sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f \ + --hash=sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5 \ + --hash=sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42 \ + --hash=sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7 \ + --hash=sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248 \ + --hash=sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e \ + --hash=sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3 \ + --hash=sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b \ + --hash=sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e \ + --hash=sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0 \ + --hash=sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa \ + --hash=sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a \ + --hash=sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5 \ + --hash=sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d \ + --hash=sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4 \ + --hash=sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c \ + --hash=sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52 \ + --hash=sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5 \ + --hash=sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d \ + --hash=sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1 \ + --hash=sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c \ + --hash=sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18 \ + --hash=sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7 \ + --hash=sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188 \ + --hash=sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218 \ + --hash=sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2 \ + --hash=sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903 \ + --hash=sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c \ + --hash=sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c \ + --hash=sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234 \ + --hash=sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82 \ + --hash=sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39 \ + --hash=sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf \ + --hash=sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20 \ + --hash=sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946 \ + --hash=sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0 \ + --hash=sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9 \ + --hash=sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff \ + --hash=sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad \ + --hash=sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227 \ + --hash=sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10 \ + --hash=sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e \ + --hash=sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf \ + --hash=sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769 \ + --hash=sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310 \ + --hash=sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425 \ + --hash=sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013 \ + --hash=sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c \ + --hash=sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb \ + --hash=sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d \ + --hash=sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520 + # via + # -r benchmarks/v1/requirements-cpu.in + # autograd + # catboost + # contourpy + # formulaic + # lifelines + # lightgbm + # matplotlib + # ngboost + # pandas + # scikit-learn + # scipy + # xgboost +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c + # via + # matplotlib + # plotly +pandas==3.0.5 \ + --hash=sha256:08d24fe11a17dc33bd6e937dc9c665f9cba08fbdc9f657f405713515febe300d \ + --hash=sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6 \ + --hash=sha256:0fac0010c75e4efb6b99e249c183a8993ce0dc95c240f9b120a5e67c727b7928 \ + --hash=sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea \ + --hash=sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49 \ + --hash=sha256:2946e77e4a53cd248cbde631a12f0e51c8324ce354c3eba4d20147c1ad6f4282 \ + --hash=sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6 \ + --hash=sha256:2c0cf1dd9b55a22d105fc46c1b489af3bd42264fcba7c66297bf47a9a1d9c78a \ + --hash=sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36 \ + --hash=sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca \ + --hash=sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da \ + --hash=sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c \ + --hash=sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da \ + --hash=sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be \ + --hash=sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85 \ + --hash=sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c \ + --hash=sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e \ + --hash=sha256:679f4e85b30ddb1515458ab1e788d3e260eae369b1f78da7a3aa4cac8ebf4a2a \ + --hash=sha256:71ecc8fb7ed1a7aa4392316b5309a6347e8e7f832f38fd897846b3a1457a9298 \ + --hash=sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc \ + --hash=sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41 \ + --hash=sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0 \ + --hash=sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b \ + --hash=sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7 \ + --hash=sha256:b1261758dfb6cf12c3cff8300e21cefad30e7ec709abb4c24ac7318e6a52462a \ + --hash=sha256:b173f5951ff6b8b0ec7675e20dff3c97b7e7a57dfcce387c2d7c5afe87cb7899 \ + --hash=sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce \ + --hash=sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c \ + --hash=sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3 \ + --hash=sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b \ + --hash=sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc \ + --hash=sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b \ + --hash=sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a \ + --hash=sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92 \ + --hash=sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58 \ + --hash=sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34 \ + --hash=sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee \ + --hash=sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712 \ + --hash=sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd \ + --hash=sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94 \ + --hash=sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040 \ + --hash=sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d + # via + # catboost + # formulaic + # lifelines +pillow==12.3.0 \ + --hash=sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756 \ + --hash=sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a \ + --hash=sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59 \ + --hash=sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45 \ + --hash=sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 \ + --hash=sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df \ + --hash=sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139 \ + --hash=sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b \ + --hash=sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39 \ + --hash=sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e \ + --hash=sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8 \ + --hash=sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1 \ + --hash=sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8 \ + --hash=sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89 \ + --hash=sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5 \ + --hash=sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130 \ + --hash=sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd \ + --hash=sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d \ + --hash=sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b \ + --hash=sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed \ + --hash=sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace \ + --hash=sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb \ + --hash=sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931 \ + --hash=sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510 \ + --hash=sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6 \ + --hash=sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1 \ + --hash=sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce \ + --hash=sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385 \ + --hash=sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e \ + --hash=sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c \ + --hash=sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7 \ + --hash=sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace \ + --hash=sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c \ + --hash=sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f \ + --hash=sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64 \ + --hash=sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f \ + --hash=sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a \ + --hash=sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827 \ + --hash=sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17 \ + --hash=sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4 \ + --hash=sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a \ + --hash=sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701 \ + --hash=sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e \ + --hash=sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91 \ + --hash=sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66 \ + --hash=sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468 \ + --hash=sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217 \ + --hash=sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658 \ + --hash=sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418 \ + --hash=sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a \ + --hash=sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c \ + --hash=sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330 \ + --hash=sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402 \ + --hash=sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09 \ + --hash=sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930 \ + --hash=sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f \ + --hash=sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec \ + --hash=sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a \ + --hash=sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94 \ + --hash=sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468 \ + --hash=sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b \ + --hash=sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965 \ + --hash=sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8 \ + --hash=sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd \ + --hash=sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 \ + --hash=sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c \ + --hash=sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777 \ + --hash=sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 \ + --hash=sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9 \ + --hash=sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f \ + --hash=sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f \ + --hash=sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0 \ + --hash=sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c \ + --hash=sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71 \ + --hash=sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3 \ + --hash=sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838 \ + --hash=sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf \ + --hash=sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321 \ + --hash=sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26 \ + --hash=sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec \ + --hash=sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9 \ + --hash=sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65 \ + --hash=sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5 \ + --hash=sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e \ + --hash=sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d \ + --hash=sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198 \ + --hash=sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7 + # via matplotlib +plotly==7.0.0 \ + --hash=sha256:08b21f1244a97e7a1a699833c4bb2678475aa108b3f1989886ed0b038ebfd849 \ + --hash=sha256:78cbf7bd06d1b05bb3b8ec1b709864695229b55151b6f7530fbf55517ead6fdd + # via catboost +pyparsing==3.3.2 \ + --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ + --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc + # via matplotlib +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via + # matplotlib + # pandas +scikit-learn==1.8.0 \ + --hash=sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2 \ + --hash=sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a \ + --hash=sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da \ + --hash=sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9 \ + --hash=sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961 \ + --hash=sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6 \ + --hash=sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271 \ + --hash=sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809 \ + --hash=sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242 \ + --hash=sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4 \ + --hash=sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7 \ + --hash=sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76 \ + --hash=sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6 \ + --hash=sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b \ + --hash=sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e \ + --hash=sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7 \ + --hash=sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e \ + --hash=sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57 \ + --hash=sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735 \ + --hash=sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb \ + --hash=sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb \ + --hash=sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e \ + --hash=sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd \ + --hash=sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a \ + --hash=sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9 \ + --hash=sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1 \ + --hash=sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde \ + --hash=sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3 \ + --hash=sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f \ + --hash=sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b \ + --hash=sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3 \ + --hash=sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e \ + --hash=sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702 \ + --hash=sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c \ + --hash=sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1 \ + --hash=sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4 \ + --hash=sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd + # via + # -r benchmarks/v1/requirements-cpu.in + # ngboost +scipy==1.16.3 \ + --hash=sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2 \ + --hash=sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb \ + --hash=sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a \ + --hash=sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203 \ + --hash=sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304 \ + --hash=sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959 \ + --hash=sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a \ + --hash=sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d \ + --hash=sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe \ + --hash=sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562 \ + --hash=sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb \ + --hash=sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9 \ + --hash=sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc \ + --hash=sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d \ + --hash=sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686 \ + --hash=sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97 \ + --hash=sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353 \ + --hash=sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2 \ + --hash=sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876 \ + --hash=sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78 \ + --hash=sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc \ + --hash=sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119 \ + --hash=sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9 \ + --hash=sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f \ + --hash=sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135 \ + --hash=sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234 \ + --hash=sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1 \ + --hash=sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88 \ + --hash=sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc \ + --hash=sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6 \ + --hash=sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6 \ + --hash=sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22 \ + --hash=sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511 \ + --hash=sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079 \ + --hash=sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184 \ + --hash=sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c \ + --hash=sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40 \ + --hash=sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2 \ + --hash=sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0 \ + --hash=sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e \ + --hash=sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800 \ + --hash=sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4 \ + --hash=sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657 \ + --hash=sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005 \ + --hash=sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70 \ + --hash=sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07 \ + --hash=sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e \ + --hash=sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c \ + --hash=sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc \ + --hash=sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7 \ + --hash=sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146 \ + --hash=sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733 \ + --hash=sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c \ + --hash=sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d \ + --hash=sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6 \ + --hash=sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d \ + --hash=sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26 \ + --hash=sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8 \ + --hash=sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa \ + --hash=sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b \ + --hash=sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d + # via + # -r benchmarks/v1/requirements-cpu.in + # autograd-gamma + # catboost + # formulaic + # lifelines + # lightgbm + # ngboost + # scikit-learn + # xgboost +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # catboost + # python-dateutil +sympy==1.14.0 \ + --hash=sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517 \ + --hash=sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5 + # via ngboost +threadpoolctl==3.6.0 \ + --hash=sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb \ + --hash=sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e + # via scikit-learn +tqdm==4.70.0 \ + --hash=sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220 \ + --hash=sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953 + # via ngboost +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via formulaic +wrapt==2.4.0 \ + --hash=sha256:0191d717dfbb8e519e7bfd4775e5b9bd57e359b3a09ab5db1ea47f6025b4d845 \ + --hash=sha256:0536f5d85ff6a157ebe7e0fe08c5479943742cf1ce59569075a66159efcbc495 \ + --hash=sha256:07daab5babb7edaf89413f5c8bd638474540fb2643b5dfb685bdc0680c96803a \ + --hash=sha256:08d8378c4514ac8dcc0ace76044cf87a873e6a52b5e6109834c8fb9037f4441b \ + --hash=sha256:09064c7be688c38c3ff125ce86bc26b69b5d78dd56062c3ddd9c814b2a25f1e1 \ + --hash=sha256:0972cd025f4c86fa2d8abd953d9f875779935343af58b4ce019ff89573fc65bd \ + --hash=sha256:0b8851a54b137eec9a8480d73cc1a309613e6a465d6f157300f1eb6b5b7c0505 \ + --hash=sha256:0eca69c9e93518240abe8801fb9b2726116a6e48172e4564c2651a2e14521747 \ + --hash=sha256:11ccb5f3de2047ef91408464abdc04682e40e7d7bc9614885d2abcaa7e2ef149 \ + --hash=sha256:15bb88c0a6c6312244917bb0a094368746fefb92663209363e16f20972a57b34 \ + --hash=sha256:1656de3835f760781c9b974bce07d8c04edb9c9ad7ad67264aee69cd68a1db09 \ + --hash=sha256:174f3576dacf55c8a7c21719d4b7c8088efb991888db5728cfc891b80b28853f \ + --hash=sha256:18aabd9301d06026f5900538051773d6f87f65ae02cdc60de482df978513dc0a \ + --hash=sha256:28f5de1526831b8f173889a436e289fe181ede8c66c9feb669d1aca8fd602eaf \ + --hash=sha256:2a9f1a2f75bb95257cc5744e255e10a5a86e923f328b40ad3dbf9d8d03430013 \ + --hash=sha256:2dc0f6412aaf5fc7e6a3abf119b7c671dbd026303daccac20112c046a48b68b9 \ + --hash=sha256:325c24cfddb46f93c931cf37fa3a9929ac94e70a5627efccd51283f9fd69c6db \ + --hash=sha256:328eb2d978ca3a6ae25f8d8fe560bf8f4bc9778b5932e7b142664eef05b92e8f \ + --hash=sha256:332d9bad7e9b718974bb2a576504c4956f45b4a0fcd7b3bb7827279167550464 \ + --hash=sha256:3367a5212212c9393e0d3ca6ae029b3a8fa40c5896e4a985d43fe8a4b8322f0d \ + --hash=sha256:36b56a4fba13b34ed8ff307557325fff215de0a58b5dbaef2c50e4d8aa39dbd1 \ + --hash=sha256:37ba372e9ae71ec43e165b5db05f52e71f7c07dafb9d6a254ef7128112dce751 \ + --hash=sha256:39cd68df4dff79f5336f9c745c06259d204bcb42d504040c9c91eac9e2abb39c \ + --hash=sha256:3a69161cae7f0dca44c89c1d14146b4a0508a0c3cad98b3f2db1f4e9016c94ba \ + --hash=sha256:3d5e5eb76fb87e62752af751d2dcd9d1cd986b12037d2e1363d109ba716029e8 \ + --hash=sha256:413e757dce7a43fcda8bb8441994b1127492ffac6a5803af777d44516df8c6e2 \ + --hash=sha256:430fde1a116df3ceb5c29035de1da6609b70e680d9b8ce3ee624422f3fe0978c \ + --hash=sha256:4597d19904b4aa97331d8bb651ac626d9397727e717942cf11bd7699ff97aa45 \ + --hash=sha256:49bb5a572469e0e18163a8ec2aa972135a0929899ecbe627665f274506e1b5b4 \ + --hash=sha256:4a1d591386ec4aa99780f672232868620f4f3b63401e2ceb529762580ef8c54e \ + --hash=sha256:4f8ddff4bbb75916be36da5169b8b9d475b59a1bd24acdb7551bb2c71be9aaac \ + --hash=sha256:52f01626f1d2bc54585954cd8b4931f81003b0ac8dad61c741f43014bc9a0f0b \ + --hash=sha256:5b8fff692f74782de89ba9d7b526a7cc398569b6a988ddc848159cc033c86237 \ + --hash=sha256:5c5c4c728cd22a36e4b8bb5df4a7d3bccaa865d27725b36eeb3b6f18fb2e1bc2 \ + --hash=sha256:5cc2e7c7b6032e11a2b367a9baadaf0c5241feff2d8205260d87f1aa6dbdf84b \ + --hash=sha256:5f041ed6a4d571010944bd6cfad9072db463e1851877b6d3227467a44af37456 \ + --hash=sha256:5f3bdfc35c83b562fcaebc0f24593045e5ed9f3b633adafd35222718a0ec38fa \ + --hash=sha256:635cc171ddfd72edff10e295a02daa65edaa1c0ba619ad11eeed15cd2258c5df \ + --hash=sha256:637fd6a18bb668a0c27b4767dcbc2fa93119c90da735bd2669fdde2d7b59fab3 \ + --hash=sha256:63b94f401d7ae3a9a3027472fd3a3ff38afd2ed293b2f0b3b84a6d133a9f99a3 \ + --hash=sha256:6436e2bda993a3eb69a1b317fc831c8ebcafb5704c390859ebd49f81218c4bbb \ + --hash=sha256:643e45aa88698c8aae938c50e61940985d4ab9e53ea666d3e8e4eb86a4820d0f \ + --hash=sha256:648d1d4f94e8a0a1656675c755f40d2f0ee5fe92c449ab45326f4ecc2738cbe8 \ + --hash=sha256:66e7512c0d324cc37bba1def2be1fc365cbb685d3aa393a8f6f4d2d00202881d \ + --hash=sha256:694005fdc3002ade0f21641408c588028abde03c85961f3ba7727d8bead3ed6b \ + --hash=sha256:6a48bc764deb585d3b8862544ca12df417109984e018767b4ac9aa46bbb55ccd \ + --hash=sha256:6b3e082d43f592fcd381aee46354a11ce887a813ce5bbcedd9766fd681723c09 \ + --hash=sha256:6b9b32d5e4f0a179cef5075cc79b79d6d3482c44c434c12969e48c6719e06d95 \ + --hash=sha256:6d57264c9dfcf37d2bf0b0fbec68d0f6184fc5617267619ada04d03e8b0231f3 \ + --hash=sha256:7082fc1f94b020ac275870c4af71b09cff22876fe6e9c4c0ad01ea21d217b288 \ + --hash=sha256:717dd7ef439863933664951f89902b7e0ee3652293543cb9917c2e16bbde1949 \ + --hash=sha256:72826910a1cf5a081234720fd43011304b899acfee219af49148155b4d795533 \ + --hash=sha256:75529a2fb569a671cf162f762c1b576f569f571b55ec7f3481258ca842ba507f \ + --hash=sha256:75944792cf6b99262d649d55710bf5901f7013fbb212c7a1d736b97a20517607 \ + --hash=sha256:788e473d1a6786d29d577b1e2bd95e214c09cdafde84907c522c31069c9acfac \ + --hash=sha256:7a057d376d994da6bd1bbf955ecfda699aa7353826f98847f5605e1801abdfd4 \ + --hash=sha256:7a27d9653e0f88aa06598954337a545fc3f75bc811df897157a8614846d18d9c \ + --hash=sha256:7d28f8f35a02d49f75f57fa4e755db4ba33f65841c0de64cd65b253916f5bf06 \ + --hash=sha256:7de5b8d94417e55c02be50cc226e0ae1209bbc73813bf691dff3979c94438115 \ + --hash=sha256:811a36628d8b76724b980d508d576e5c5ecae1073b6ec4b4eb21646921906fe6 \ + --hash=sha256:85ed3c67fd39e8d9a36c224758cb6f2f4eb277d07ea677930caa0008c18ec002 \ + --hash=sha256:8763ad01e3725b7751a4575f38bbcc19c0aa0822fec91c5c5bd21ce3ce7e1d2b \ + --hash=sha256:8828369b7d3e93c547cc8ad931b5a57b4e8d174035c82762fb1091e7d05ac9f5 \ + --hash=sha256:8b464316489fb2fca0669ea0f8f07290054a0f26fc72982d3e4cf95469628ba9 \ + --hash=sha256:9125c6dbe8b88c00dd8ef4fc1e55757e8eb4720b6b2b2cc610a45bd32bd28c57 \ + --hash=sha256:93180c2199784dd6a1075b33f9ed636bd0966821edbece6b3d5379b1c4f0bb7d \ + --hash=sha256:932a8892265df7b71257c30e5752635bc1f06a8c4e264024ff031bdf9bb10918 \ + --hash=sha256:947bd4b3438167b3638bf5477cb83a068a586ffb6d331ac427f39839c2b93b3c \ + --hash=sha256:9905bceb7b2833559518574ad6259d2ec9ffd111a0aa330ca685db74478e1ae3 \ + --hash=sha256:9c884240e7415d3a384e70a15ceea0e884cc9289bcc254afd6412d4e7cf99c47 \ + --hash=sha256:a1117c63a39ba4d1b884e658089e512412d5174217ea1b4fe570977e42a5b129 \ + --hash=sha256:a112a1bfdd2621e4344cb0a32dbaab80636b32dac1b055d03fbb2a67d806d1db \ + --hash=sha256:a67ec80d15ac199d4a9a04a33f3039a1c219c9bf1c07b1b0422497613f167fb9 \ + --hash=sha256:a9ca1cdb3f7facb4990c7739ea5afbaceeb6728d066feedde03a4cfe83b29b03 \ + --hash=sha256:abc347e92f9202c8ac1d5c1626a800fd5e56e13433f0651b26dddda5b421ac79 \ + --hash=sha256:b1737f46b1e4a81eb93500a7f2854319e1c7a86e8863fb050b7b4daadd5a4178 \ + --hash=sha256:b33df90f3d1e5b1c8811830b11a3e718b4f3a2823b748fa9be1688cb82b193f1 \ + --hash=sha256:be535bdfbedda84cb8ebc6a80955dfd03d46840c13470486bd038f089e38b172 \ + --hash=sha256:c246aaed719dcdb62eeb7b8d9306a6237777226ef3baad35919c4ae134c91ce7 \ + --hash=sha256:c4b42c92df24a986da7e2b5b44fb142504d858c54a276f9f366983ca4482dfda \ + --hash=sha256:c4fca1e63af6675af3df7cdfcd5a0c878b5e655c7e48611ced9dc8d62183a11d \ + --hash=sha256:d294576fddac636589e4deccfe782e8f429da10f167c1985c4d51071de3672b7 \ + --hash=sha256:d5f45bead708e2c0014be5e98531ce7202916b098a208c7be83c6ceb0a2559fa \ + --hash=sha256:d7c496a966d70f8caf215faddc00de9931a5bf652664221b785a73b20229a696 \ + --hash=sha256:d7dbbdbfdacb85c2d962fa52db791c77943fd777d600d74c95af2d53b32f5a94 \ + --hash=sha256:d8e6e1e5dc684dfce7c33fc8b67a08ba2af94f3a45cfc70d5c1d6a839d2caf97 \ + --hash=sha256:db1285071ea09a7767fac608e7b5c7b03c09833b06186875a359905fbc659d29 \ + --hash=sha256:e084558fbd112d2e1e34b0f5c71e45a3405bdad51a17150368a959bcf6697964 \ + --hash=sha256:e52c6a5be3284719e53b629ccfa565c146e604e861de35e861c94f7622806eb5 \ + --hash=sha256:e78c947e18fadfd690c9420c30a96d221feeb93fc8f1cc00509b370ac16c3114 \ + --hash=sha256:e8df31a126a0a247c1aa379e30873839de03912dea09ca360c680f3625d815df \ + --hash=sha256:e9e7e94472f0e3f1447caf27e1939eb384d0e87972a35a05f5c2e0968e9c01af \ + --hash=sha256:e9f8017443595870aa31f46125553a5c55ce95a26a267b96261baee6ba566d83 \ + --hash=sha256:ef4e2d6e399ce6eecc80179a6b9ef6544f121288f95fc132bc36c9d9503903af \ + --hash=sha256:ef9797bf7c6f9ad9d294538c4f9a64ef3dbbadb63590a9a067393fd49ba28b0f \ + --hash=sha256:efd9a4be6785295e471f71efdf5682bd11d5b822b9665e6e1b4844917cf2f7ac \ + --hash=sha256:f1e9e088094f4895f84ab043e7d59401df137d663efbf1e80c82144882960830 \ + --hash=sha256:f43af38a642c3d6062e9740d8f5cc0feb5dbe0da516702df892147393b8cb14d \ + --hash=sha256:f53837b56ca834f381d300621f8c9525b9da517331ce0f4b805ed08f63bedcd7 \ + --hash=sha256:f7fed45dbadf5d98a52bfff9624d3cca00affeb9543d493c9632b7a53cdd35c9 \ + --hash=sha256:fc1b2cebd6d8db9b4ac0adc817c08b4901922e85604ae2a69aecb5217b2c09d8 + # via formulaic +xgboost==3.4.1 \ + --hash=sha256:1ea15f15f661825b6a67d87674fb9604a1abb38dd0d4c5cf0486fc85f5203e83 \ + --hash=sha256:2d30fa513673101f542fdcbd18f30c8f96c064046f798635ac08663e9969f81b \ + --hash=sha256:6968a4c71efdfa859df0dfcad0d99211c95c28c4ffd6aecff46efff77d18026a \ + --hash=sha256:6adf2afa396da2ae8ed30295b50b99d4712eed9a6e0ce6cfe069290e4335e51f \ + --hash=sha256:7faaf99de26719c22bfae883a02bd56b5a3c2203122616e563cc72b7191b5c96 \ + --hash=sha256:a7afd7dbace0951c93aa85ffe046e54bc40893f5b51cd3e7991eb157bf9c7c7c \ + --hash=sha256:e9312b30e5679d27c1d8b9ee97e092b964d960a672d5d406d9fb3cd0845c9797 + # via -r benchmarks/v1/requirements-cpu.in +xlrd==2.0.2 \ + --hash=sha256:08b5e25de58f21ce71dc7db3b3b8106c1fa776f3024c54e45b45b374e89234c9 \ + --hash=sha256:ea762c3d29f4cca48d82df517b6d89fbce4db3107f9d78713e48cd321d5c9aa9 + # via -r benchmarks/v1/requirements-cpu.in diff --git a/benchmarks/v1/requirements-cuda.in b/benchmarks/v1/requirements-cuda.in new file mode 100644 index 0000000..4d664fc --- /dev/null +++ b/benchmarks/v1/requirements-cuda.in @@ -0,0 +1,3 @@ +-r requirements-cpu.in +cupy-cuda12x==14.2.0 +py-boost==0.5.2 diff --git a/benchmarks/v1/requirements-cuda.txt b/benchmarks/v1/requirements-cuda.txt new file mode 100644 index 0000000..f4f6da8 --- /dev/null +++ b/benchmarks/v1/requirements-cuda.txt @@ -0,0 +1,1276 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile benchmarks/v1/requirements-cuda.in --python-version 3.12 --python-platform x86_64-unknown-linux-gnu --generate-hashes -o benchmarks/v1/requirements-cuda.txt +autograd==1.9.1 \ + --hash=sha256:7818c5c69ddf9efb7da74097bd741a4c7920133f41966f68537223a15ef798bc \ + --hash=sha256:b788bae3fa010cbffb4cfb7b8ba2a3f0daa6072a8506da6164c779fe9cf3e05a + # via + # autograd-gamma + # lifelines +autograd-gamma==0.5.0 \ + --hash=sha256:f27abb7b8bb9cffc8badcbf59f3fe44a9db39e124ecacf1992b6d952934ac9c4 + # via lifelines +catboost==1.2.10 \ + --hash=sha256:19de3cb267be3ddb8fd667a87f9e7d3c9ee31783c61ea9e6e6f036f666bddcc3 \ + --hash=sha256:21deaef3f6f49e70b320ec48f4741133287e888297c42af8bd677ac636e8fc64 \ + --hash=sha256:22aa943cc6f7839ca5d3d66d4f8763d8c799fcf43d64d209e14e2e66016fdae6 \ + --hash=sha256:25c9b0dd9afb464efe7ccabf7567241aa566f70e7f77893218cb9fa21663e5d5 \ + --hash=sha256:26ae6d423acaf0e9d8160f2477a990431057ed04522d993c2f42dac62743b4f7 \ + --hash=sha256:2a19c1a9e92c76fb5dc75cf6a5b0d03127f3a36359e1e02e5d139e27581e2d57 \ + --hash=sha256:39234b3692b6c9002b4a2ac529025fc210dd72feb9b621b27d17c65b7d3e9f92 \ + --hash=sha256:3efc5e4d414b7c13bff6dd0d6c938cf09bb1445097283c7790e54b8ee461820b \ + --hash=sha256:41bbe16cab0695978c325a20fa300f92831ed78e9cc8c5fe8047538b4055e98e \ + --hash=sha256:42c1b6c7ae5c18cdbe00c8b9493987cc13338fe328baaf1a0b98ddaf58db96a2 \ + --hash=sha256:4debc33c278e431681d47d90818c15ec58407c8ea028b3060953dd29a6246946 \ + --hash=sha256:5319c7f9a7764d7dba04c218fd28383b7267553f83232e8ce8737d6b8d38534d \ + --hash=sha256:56c2c0ec0c16874b83d39f892b7f8a026bbd7404d59b23a34ce53f6b4b87b26a \ + --hash=sha256:5819a880af6b314f4980e6c26ad0f7552eafcf247d521bc884fe726347fdd87d \ + --hash=sha256:59aa166f075f0a5ea57b0ba46e5060bd6a22e849e91e4142f16c2df11295b184 \ + --hash=sha256:5ede858e634d6d0f521bf6dd6fad9374f23d37049ee48e0779ccd2a372632cb1 \ + --hash=sha256:5ffe85f53092219cf65c73c2946426a289ef6f62c119c2bfda52815250d9bcef \ + --hash=sha256:6b8a7ef11d7a89fc547760cfafeee895011a4b92cc1f60d00235ef80a71158ed \ + --hash=sha256:7b8cc4ea3a6ac4a8d05f3a79c8ee5454360a0a710fa12444963865ad3f0ddfec \ + --hash=sha256:951c5bdf27b8edb6ca624f41134888c666ae68275488803d3c91ce83e154f0c5 \ + --hash=sha256:a1eea0b556d1c154907a6896eb865e1bb39c9b974e0765d879a41fbf87d4639d \ + --hash=sha256:ab2e84237308d62bae236b1ecba2e3867697f96bdbaf0ca68dafc2c886946406 \ + --hash=sha256:b27115d5b443048f710001c8ac666892dfe03498492310b00466203c91cc30a5 \ + --hash=sha256:b28f763776e62f50da90dddf73b36399583295032667a7e46fc5c1f2593eb80f \ + --hash=sha256:bad9a70890cdc591080a908d54a3cd70002ab1e48b2017adff84726da0b3e16d \ + --hash=sha256:bd3d3b344894f61b5f70124658f302148bb9a51c41d0d5b6c453a72e9dfefc49 \ + --hash=sha256:c20dbca7fb73458e7f017faf091b91faf3f106e113d6019e8ecb99c452169426 \ + --hash=sha256:cf54c216f6b3b102e06a5fc42deeb7a2497d622e6bc2e222f586e7e357a942f1 \ + --hash=sha256:fc040b85d06588bc0d22bc4941208f43b4a56fccd4ff78b738ee823956b89370 + # via -r benchmarks/v1/requirements-cpu.in +cloudpickle==3.1.2 \ + --hash=sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414 \ + --hash=sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a + # via joblib +contourpy==1.3.3 \ + --hash=sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69 \ + --hash=sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc \ + --hash=sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880 \ + --hash=sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a \ + --hash=sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8 \ + --hash=sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc \ + --hash=sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470 \ + --hash=sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5 \ + --hash=sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263 \ + --hash=sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b \ + --hash=sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5 \ + --hash=sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381 \ + --hash=sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3 \ + --hash=sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4 \ + --hash=sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e \ + --hash=sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f \ + --hash=sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772 \ + --hash=sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286 \ + --hash=sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42 \ + --hash=sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301 \ + --hash=sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77 \ + --hash=sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7 \ + --hash=sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411 \ + --hash=sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1 \ + --hash=sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9 \ + --hash=sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a \ + --hash=sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b \ + --hash=sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db \ + --hash=sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6 \ + --hash=sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620 \ + --hash=sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989 \ + --hash=sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea \ + --hash=sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67 \ + --hash=sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5 \ + --hash=sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d \ + --hash=sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36 \ + --hash=sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99 \ + --hash=sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1 \ + --hash=sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e \ + --hash=sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b \ + --hash=sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8 \ + --hash=sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d \ + --hash=sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7 \ + --hash=sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7 \ + --hash=sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339 \ + --hash=sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1 \ + --hash=sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659 \ + --hash=sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4 \ + --hash=sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f \ + --hash=sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20 \ + --hash=sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36 \ + --hash=sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb \ + --hash=sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d \ + --hash=sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8 \ + --hash=sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0 \ + --hash=sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b \ + --hash=sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7 \ + --hash=sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe \ + --hash=sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77 \ + --hash=sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497 \ + --hash=sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd \ + --hash=sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1 \ + --hash=sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216 \ + --hash=sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13 \ + --hash=sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae \ + --hash=sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae \ + --hash=sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77 \ + --hash=sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3 \ + --hash=sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f \ + --hash=sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff \ + --hash=sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9 \ + --hash=sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a + # via matplotlib +cuda-pathfinder==1.8.1 \ + --hash=sha256:ae0137ff9e56ea97499bcbf54f5f2778ec25f3266715ac86da192a795af982a8 + # via cupy-cuda12x +cupy-cuda12x==14.2.0 \ + --hash=sha256:0d3205b1ac1093b6019530ba3b7f7080e2283820f452f0c543a3560d58e0b9bf \ + --hash=sha256:1c775069f0af34662a8d4ae90848e29afcaf4ba63762d556ff22b6011683e571 \ + --hash=sha256:2d0c77202f5ac5920a420888b28200a11d03d24352b7585d3cb1a84f67fbc96c \ + --hash=sha256:5f08fc1d651d2446c1d18ad94f1a710224fab36d46634d4aa356423926964591 \ + --hash=sha256:5fe2366cc5c61a7ee4a527ce1e8951cb89092d0fb0b5830623cf114d1942c585 \ + --hash=sha256:8cbbd48c9cfd6b78d0a833ebbafda3e1b057c38d6acc3c6e54de0735a7364e27 \ + --hash=sha256:9dd33f9cfc7aefbd935879bf50e95db539721a0702bdb05be3c74bd46a85ba29 \ + --hash=sha256:b74340aa7271f0f081f77e2e5107bac75af19b86df29213db7ada90e14428efe \ + --hash=sha256:c9571d3b5f2e65758137e210f7fb3c3b34767f0af6b6ca04035a244b6141ee12 \ + --hash=sha256:cfe673f73599ee0b9c2c9de5c0bb2395d98c9238c24deafa2ddcc69cacbd6af6 \ + --hash=sha256:d14b651ed835079f8a5e273936e02eda690be7d30f2658e5f48f328322fd9d7b \ + --hash=sha256:db802e4b9a85ed84fd3e84790586c06e808ee45e0214cd4e80734c09fcf93073 \ + --hash=sha256:dcea9f2b1887ac631a9275a61577e09d1eea26bf5f95491501c3b7528cebc592 \ + --hash=sha256:eceffbf02a5833c8ba1c94615da07c374284db76a60f8c8b217b0d9d2667162a \ + --hash=sha256:ed317136439af4780f217eda0b82f25180084eb16c44854e1bc9e055f96fd429 \ + --hash=sha256:efc1da23505e88d9834a3ddd3c00352c34e58e301f512d9dd593cc4bfbbdf7dc \ + --hash=sha256:f22a4408f47b6baa791de395efec8dce8fe1d03f92b50867af6d7d25e6fb0272 \ + --hash=sha256:f82141761f2c81905d49387464ae29438887956d99063381c93a1d5d1b7d32e8 + # via -r benchmarks/v1/requirements-cuda.in +cycler==0.12.1 \ + --hash=sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 \ + --hash=sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c + # via matplotlib +flatbuffers==25.12.19 \ + --hash=sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4 + # via onnxruntime +fonttools==4.64.0 \ + --hash=sha256:043f6c572bf236f2a76e762c25f841daea11e8fc03e78088d7be66e0c5b4e4c0 \ + --hash=sha256:06b6409b868494556a831ae33b2d9a090476c37516b38d70f45a9720b460d423 \ + --hash=sha256:08f172961e11f4eb4f80f2f20049e09b0ea8e044fa6d456fed8346eb8588f360 \ + --hash=sha256:09657817b75575822bcd6098ef0ebf0386f34430839ee53109e70fd40a7f6539 \ + --hash=sha256:1c3661324f3f0fa4539a32288a3e0711a5f3ccf020036e760bb558ae9811a16f \ + --hash=sha256:1e4e84b47839d35be24dbf476845a34f2ccf99707b66df125c1c414d3e86d25d \ + --hash=sha256:236e59bc7e2a63557a4d7b013f9cb9e28d9aebc45bc09f85e545e6bf091db626 \ + --hash=sha256:2524a26f8fdb9051b0d778d052f5d238285ca9f91a7dc004514c7d6cf38d35f4 \ + --hash=sha256:2730946ca8f12c356bd98eb9b2b095c8e761ed05bed5afb0d5b380cebe4f6370 \ + --hash=sha256:2c42237b7e8c6813643e57d3efed3be094d4c06339dc2166b626e2cc5c12ee93 \ + --hash=sha256:3200180abc69639483cf54a17cca2e13c31ede5f665979ea0a9c829d093f372f \ + --hash=sha256:398b14f89ca950b288bd290875f07e4e10685644fa4ac668546fb107b1ada4d4 \ + --hash=sha256:45e3ecc3888f1637094fd75cd8fc727f3a4b06d1ddf89181126c071e244fd2a5 \ + --hash=sha256:4691a122b8c1d0d82d6e7510ce59d5c42146518240274b53e912e255573924f7 \ + --hash=sha256:498f02ea92c9ca18c0f9c581ea93184a9d56c25b0af14189b0767adaf34235d8 \ + --hash=sha256:4a05783ff54ce4c7a28f18e5772efdf63c219374bd9ffc55452182e1cef8be60 \ + --hash=sha256:507c553cdb5abe2e951b5368423849fe29911a828c2135319c3e500e3bf25b32 \ + --hash=sha256:50e52b6f479ddb1fe32423c2ec860811f36584cf6eabf279fb9a4f98b859a8b4 \ + --hash=sha256:53eee22af5b5a305c1ee2652955ed46b148e881456fcec1e7f0eb27f642f6bb4 \ + --hash=sha256:5af87d1a6d247d7467ee082ae977a5443b2c45f8cd4d59375b6daa38d523c2de \ + --hash=sha256:5b90ad6637237b636d15c9ae8b7c4a7a1c194f33def378677e468c13fd4542f8 \ + --hash=sha256:5bfdaada437e7730c17d366bd7bb8c4a16639963ddbfc1b2f302a68a17a290e7 \ + --hash=sha256:66a83f93579fb3493e458c4449d1d566a7b2a1c7b19915cd0fa3c9b8b5a8540b \ + --hash=sha256:6786bed88581e19bc4f28ea7a64ad531e8f54acf50327fddca942688824a60bd \ + --hash=sha256:6946c033a144086d5b98c976b72f476b70c93fbbedf914eee0e886f073a4e9fa \ + --hash=sha256:6eae4376adb104c2acfa76fd9ea0cb12b572ca1d70eceac709871f638ff76e93 \ + --hash=sha256:6f1ce9ef9a1b13098efdc2e43a2ed96d9851bbde7b31c652a87552c4efe9b422 \ + --hash=sha256:70fd99e5a09fb77f14b29d70879a4fce9529b2d2948b14c96708e0a61e001b98 \ + --hash=sha256:730eed859508cb7b0775ebe6bb39f18901f168eb989d8ee23a4fe082700e1e3f \ + --hash=sha256:769fb64412ca237547ca73f111a64252d9e32c9d938bed51ed537bc9146a8f54 \ + --hash=sha256:7d7995b906666037d7114c20a5566a372902747452af7d5bd4cd6bca8f1a2550 \ + --hash=sha256:801fd04899d72eab34f02ab78d0451525621b3bd589da9d2d480dfffe951b643 \ + --hash=sha256:8252f20108e557532f91d7d6dd9af87c16ed6fa930f65516aa480fa2cfed3363 \ + --hash=sha256:83cc48d1411d2ff388dab99973dca81172cc9ceae9c9799da9548d494cfb38cb \ + --hash=sha256:89356c0793b474af7e49ec90d39fb2363e2341516a90460e38231df5ebe8acd5 \ + --hash=sha256:8dd18fdff0ac9759b8d67a714730abee07b2312e3656c20ba5affb0107094762 \ + --hash=sha256:917fd520bb60809d83c14d43cfe48d5ad2516abaf2c073d65a431800dade2d29 \ + --hash=sha256:9443eefff58aad558608f352092e1be6d278980e8c3b4e8621fcbfda97818500 \ + --hash=sha256:9ecb2b206b5b2386f6968721a0770226b66bdd54adc4279bfff3ddf62873eed8 \ + --hash=sha256:a0afa8bac675445dc0e2ba2891ecbedd9be89cb437afa94c823e0290cc2c4bc5 \ + --hash=sha256:a3238a693e806a3158375c6403b8f6f71d86eb9c149b60c97f26dfd560c98ac8 \ + --hash=sha256:a515f664cad988f2295056833a59f62220bc3e46afdaffe389a29060f6712355 \ + --hash=sha256:a8c631303bb1fd7be3067c47536a30ff1fcb4846d6008c112bc52a03f7cd6965 \ + --hash=sha256:b2763e452b025ee8e990f0462e76052de9bb094ebc21d296f62c6dfe958886b4 \ + --hash=sha256:b4a7af455ffed980925bc0ebf5b8d6239e6c3e797d9d755b6db192fb3080d614 \ + --hash=sha256:be084d19a3ac0c8b2aba696680642d703118d3b1f18cf83f5b7dbaf0ffc62ab6 \ + --hash=sha256:c3c1fb656063a2f762db5378ea8d38ad5f7836b4f3fb8c4652270ded43df2935 \ + --hash=sha256:c60be0aed97a32c6ba8cee21f0d0477136e495451bd97910f589ac892db120d4 \ + --hash=sha256:cf67f96dc0bfe9607f5f2b734cedfbe2f6f995231adee4ccefa12872044d452d \ + --hash=sha256:d16102cbcd4615b09c64e6022733faccc93200785f1ab0d4493afb8b0261edde \ + --hash=sha256:d30c966bea2deffa19c738c81776f7182da5ccabd97e666bae4f3d6ba87341d9 \ + --hash=sha256:d652592c71683941b768306fa1c7c6ce1bb9b072505043feafe86305d71030b7 \ + --hash=sha256:da4c9bdeaf6b06c12d13d0addfc8ef15aa9695d26574a6dc10751258bef72f30 \ + --hash=sha256:dac25768be4c03a990c359f408cb7e8958ed0e93061e495b3642ce7909761205 \ + --hash=sha256:dc96150f99e05a317cb1f042b92c4cf8bc93cdb1f9f85717322e202ecdf2e505 \ + --hash=sha256:de8acaa5f4160f537a3cf41b031171d51004b9f4aebfa6c194f18dffa9533d03 \ + --hash=sha256:e412767d1c9765cf1b82f7b00f1686c6ca5809ebb77af363b3f9f2325a465c01 \ + --hash=sha256:e4812f71c39d77ec5041348dafa400532adf7bf8f1fffa9aa6495fce5876d7b8 \ + --hash=sha256:e63b63b8b5fdb8e29318dff2b15c5f852be46e972775b466f75b848f6eed4502 \ + --hash=sha256:e662f874ab2c7da9861584db44a13573e0936df087215f63013138f6e5eba083 \ + --hash=sha256:e7b34209eef39462563c05ea9dcf51c272a2ded56f5753da925e66bca3baa484 \ + --hash=sha256:ecb2e59a7bc692fee64dda6010deb66222335693b30046f15cccf81233aa715f \ + --hash=sha256:f521d79d6acda4923b264805541696f452079db0952a5bb96f9ff742f50629ec \ + --hash=sha256:f8669ce37851b597d3435b91fefa51139e58d506ca449ca0e5bb68c63b8b6d2b \ + --hash=sha256:fa75c7970bc6bca340cc6e20f20f069201bfcb50094c31a536fd99724d1d01ca \ + --hash=sha256:ff7aff4637fbf71394df139c63ccfe08a47aa4252d2f91224ddb3335c716c925 + # via matplotlib +formulaic==1.2.2 \ + --hash=sha256:0f84ff49e3fc9dc0e68ab08a0a9427874021aa6c558e66b44dc634a35739b09b \ + --hash=sha256:c99e8f11ff7d327eaecaf63855ca69b7fa0da100ad6c0041ef80912fbac667e6 + # via lifelines +graphviz==0.21 \ + --hash=sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78 \ + --hash=sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42 + # via catboost +interface-meta==2.0.1 \ + --hash=sha256:902bd9a95a12f195f15753a1080075d4eca7a2cb934fac7ac03c9e362b50796a \ + --hash=sha256:f38016bef9a4429b6d0792d809be7b65e9781820c674bf7f463999086b6e6323 + # via formulaic +joblib==1.6.0 \ + --hash=sha256:2ccc96785b12046c08fd6d55839c12857831b54a3c1673ffadd2f04bfc4eda03 \ + --hash=sha256:3dbbf9f6e4b592a2357b854608e980fe6390d131d7a82f011a377ef2ebef7aba + # via + # py-boost + # scikit-learn +kiwisolver==1.5.1 \ + --hash=sha256:007a5553dfc4f4e8d184f588a0200e2cd4b63a59cc8796df3c39909e679dc7a0 \ + --hash=sha256:0324cd2567259b7a095f6cf18a52b0ffc6f3de9e69528ff1bc0e7a37bd43ff1a \ + --hash=sha256:0627b9bceb9c3cdcf12b8a18655eedfed2692b038df27423383c120d0b7dc2d6 \ + --hash=sha256:06a6917674de9e0fe3f66f5430787f59a9f2ddb64af9b714eaec547e29ef5c19 \ + --hash=sha256:072bdb15a3c19a5b5dbc8f8fb1f4e1884bf4f3507eeb4cc6334401274d37a5c0 \ + --hash=sha256:0a4faea5c6db201c6a21391d2ac926ea97acf7dacdbc3c417189e1adb1a00837 \ + --hash=sha256:0ba9527afc80ae3d7814ed98b6572d02bf85eaf48065678342c5f0c6dab7a8c7 \ + --hash=sha256:0d8924877ce22e17326a99a418c3c82037da078df3c6a260b13eca677444e6e7 \ + --hash=sha256:0ebdef3eae5336568147c39a55be6a2036ffde53faa9ca2d978989ae7c2da12c \ + --hash=sha256:1209042a623ddfda5497e4066c7b77651dde8e1d3a9dd97599dc7e97f3b9b78c \ + --hash=sha256:16895f553ee6620a827d2da56b871f835fb70b9216cca5d188e885caf6e3bd23 \ + --hash=sha256:17851e5dad4484be0cbccbde3b15331deae036de9aebd45eed964487802b172f \ + --hash=sha256:1798e83840c3f627246104c4d8a9639c60fa068adf9ce92b61791781fa8a68c1 \ + --hash=sha256:18170a77ddfecf40ec60d0928268dc95880c881864e015a8f34094ed18b9b9ad \ + --hash=sha256:186884a58486651e3c217b6acea0a53eaa9498fdd472057c46f2f0fb5c25aad5 \ + --hash=sha256:18a0cfb124546a4c2e6087c5f3029c7f44b37c85b142e0ced71f73a7599ac208 \ + --hash=sha256:1983f0974a750a6f6556f368ba11105d1d8369c735b944747c9f12ae5aea7aae \ + --hash=sha256:1a7587dc335f2c0f5bd577fd0540bd16c66006bdb60f759a1059f025e6c4f071 \ + --hash=sha256:1acc7e5b7ef05e9da8bb70cd6c7c4513090213d2e1ad9720f599f0bf6c52aec5 \ + --hash=sha256:1d852545c4d0e35a72728d072cbaa59e2fa7dd84bdf01e068d670dd0ceb58eb6 \ + --hash=sha256:1ed0f5e49d0ceff8b72190824d9e59c062fbbc02c231b853112c78474b3f5ec2 \ + --hash=sha256:1fff05e239575b1481b6ed1a782f6fad616efbf1f0b1f44e6e85c4dfe426e483 \ + --hash=sha256:21e46b23a2da695c364124817bc01d970effd5483147f8d66a6a7167e3f6b851 \ + --hash=sha256:22d5e5aaad6be121f2515765e3b1c444352cb8eb4c86510801db8f2e50757316 \ + --hash=sha256:2551cf9917af48ee7c4b29cc82320489508cf96fd26a51f6fc124de661cd44c7 \ + --hash=sha256:255605693a483db7bd5c79f60437f7bf658f7f520d61aa42722e32257c941951 \ + --hash=sha256:26e8268480be5061d509e29669d59103c067a26377a56491630ece11762e3858 \ + --hash=sha256:27add358abe374ebaa3b8763ef380bc99051b5a4b18d94878366a9e4f59efef0 \ + --hash=sha256:2ae70bc59790d2af72a3f76f24b272403e135070340281108b447cb77ea70819 \ + --hash=sha256:2e10ae1bba1899188b33557c10d73affcc12033edd18adddb57d209039976a4c \ + --hash=sha256:3221f78211074f561c44ca42eac0619828171bec15a2c4cf6f7747d07df76e8e \ + --hash=sha256:34633ecf50d16187ab8e5528b7a2530f2feb4e23f300db4672538b51cfc5cd38 \ + --hash=sha256:34ec467940442c9943016fb2d4c81d1ba84351eeca2f1a78f8bc87f1ba0d414c \ + --hash=sha256:37f801b5d7cc0e5a548921308e059fd2b057bb42972b591cfa3049f95423c4ed \ + --hash=sha256:38f6e0deb4d0a4615efe0c4efc5990b06ae450ab50a0b321c0b078b6d238c083 \ + --hash=sha256:3c24cd69455e1b00ddf770c13b6e2c33e07d6dc3f2d34add0bf9277c5c6bbd46 \ + --hash=sha256:3cc210010fd2f438a3ed430b45f1b501fd13a8618bf984dc2c5ce5b69b78752e \ + --hash=sha256:3fa5855898f6d3d01b72ccd48a2d65cbdee301251603fefe34e2025bddba219c \ + --hash=sha256:416ba7ff9f233b7036689bb5a3783537e838ad483f63558d2a800f75afe738b1 \ + --hash=sha256:431dc224a1a92a5c8f582d96e505196a3b5997a7271076678da2dfde67b77e9a \ + --hash=sha256:43844c1a7ad6d723d5b5b4c4fc7f5bd399c40e288120d16257c7c9e8765c6e85 \ + --hash=sha256:44b8faef94f1857e77fa0238f3390ff1ac51d2ea20a487e2e452a59fd2b5f5ca \ + --hash=sha256:470d420f98d368d6f010633a20659b544c5fdfa5329e6b70219f2ef08fd4a7ef \ + --hash=sha256:482676e5bd48d70ac99d9fc78863469845421e01184fa83f1f9366dc49f7e974 \ + --hash=sha256:4d4ca09bf13cff792b1884f64b98ee6c2467930d632233be25c56b442d99f10e \ + --hash=sha256:5025e36fb4fb275cef0a4e30dbb11cb4ae61d1c83deb90189cb5d7e4cafd6b55 \ + --hash=sha256:509735237ae0d849e8a843551d423d2500d2e0a9ac1611a145658b29c0fb9f85 \ + --hash=sha256:534f02c1abb31ed6dbd3515545285c330b2f12d00fdb1fdb71658b9ca5a13a6a \ + --hash=sha256:5978c3340f16a35c30f8ab2fa7bcf559973c55f1a5ef6970e1f621acf3c4db13 \ + --hash=sha256:5b973887ff782cfd6b67c9904ad8ca542e0bc5e4961503408b423b5a688b4d38 \ + --hash=sha256:5c490db2168a508088f59140dd392556a54b8bd1048fc6383c8baff13c359673 \ + --hash=sha256:5d142e352eb13facc7dd047489aebdff6ba78576c239f1ea04931979caaf0567 \ + --hash=sha256:5daa1f19e097050b9c4d9a78fcc9263cb96c9dfae08037ddc1b7c4ad1889f2a2 \ + --hash=sha256:61e9a64c7635095a6bfe483e2ff055d437c59bd45f3617a228b37277f0185d62 \ + --hash=sha256:63fb7294b768f444eb4b068965f2662f28c2fd4161e23bd60fcf3ff27b74c046 \ + --hash=sha256:685929988b208a911f1285e2f8ed54210b0d681a3dc0f03e00d599d291986e7e \ + --hash=sha256:6a797a1cefc8b9c93170db580337e1fe3d011ad18b1299943231279406342048 \ + --hash=sha256:6b92f60017dda7d877fdc546438b5e28f31c523264f49cf5a48c1d0ce1a0dfbc \ + --hash=sha256:70ed9a45c7484d2b30cdacf60d220f494a1763b9fec1ad03285c6553fa0889f2 \ + --hash=sha256:719a35fa1156db3640555f95ebb94f60a444e64d1c69626b0edef5df78eba225 \ + --hash=sha256:74ad5c3dad54a4641b4c28cd15ded70899d04459c6c7aeacafea716be97cce6d \ + --hash=sha256:74ea337e0ec3f6f342a36a4f1b5cd94dd9affddcd28ba9aae2905af932ee8c6b \ + --hash=sha256:75d9b1cf8258462dbdc1eeda718c96ea7f079324c09067f6daabfcf37712b7fe \ + --hash=sha256:77a4c8187a5948d7f8795adb765a3c7b553d07d86d88e43038fc32fc1fb9a3f3 \ + --hash=sha256:7824b5e8bdbf0bccb4ccd37bbb115849a1dc45437fb4de8351385ed07c437ee0 \ + --hash=sha256:7d38b0c279c3032e8c9cc013b405c6df8e1668dbf15465779aa7f15f61201812 \ + --hash=sha256:7e9c01d3dd7ceba4d1d436cc021d40d592466e40b9bc7f5d83dc4e98a5c9cd8c \ + --hash=sha256:7fd82debf43c6acd0a94359d232f6bb516ee13f269a7993736a9ac9f988bb5d9 \ + --hash=sha256:824c3d763a05ea9e9003610145186b0e9848c7584a5575c79bac5a8e7cd80bad \ + --hash=sha256:828f75af2b0080c8a972e75f649ab46af008e92c6104a57a759157200b835b75 \ + --hash=sha256:83f78128fa28705fa85d01c59771c72fe81c11bd0e6155edbb9f818983a7d761 \ + --hash=sha256:876bbfd276473d3daffe30e8c975df4ed9429967b41a6cb362dbb5155b6f13ad \ + --hash=sha256:886fc26012f0e8b5f69d1cfe6d711f6b11f194621539bf8e6bb1c25c5dc82724 \ + --hash=sha256:8a34616dc2521cc8dc1d7d081734da63539f021ac0450ce950908340c6e7aa2f \ + --hash=sha256:8a708a47ade1fe19e8371d5da076bac0dd4b0a5a7985ad6c637f7f7e361b6baa \ + --hash=sha256:8af9b142ad719ae3a911ebf616bc4b78b32bbab84d6a40d3ad2f129670509957 \ + --hash=sha256:8bf4df63592c2a66b4f8edc5df2544998c288aa02f96ce0acd880cd1de8c8127 \ + --hash=sha256:8de6f2a4ce7e7bd27d23dd94abf0ccafe0e0e5cc9c764b0577191f2c25f08f26 \ + --hash=sha256:8f8fddb8e323bd6eee4e54e69a39243beab22689070f4c66b472c4cc88bb89d8 \ + --hash=sha256:8fca690b00c4c48f6c2a547b0160ed511357093a4e4c9b47e0fadf3128066d89 \ + --hash=sha256:9506e892bcc3b409831d363c6f53e5985e1c8d1f6f6b0256d00358684ff85378 \ + --hash=sha256:958254518717542d02d0688d0d20cbf771da5e415e6f49543f92481c850a4540 \ + --hash=sha256:95a02752aa032eef4aed01cda6d9b687c669bd0396bf4519eef8bba22a286720 \ + --hash=sha256:96c30002424670b5e1e46495c2b8cbffef39cf77c1d79e76462029d50339785b \ + --hash=sha256:98b208a7cc42c803445ef551d6753cc42a5ea13e9cab1ee66cd8b9cb70195330 \ + --hash=sha256:9b3092d8992a1d69b7a59c3e39f35e1b9be327a17f68a7c35fc17329e337d6f2 \ + --hash=sha256:9e51c119992ea8820706871c30a4642ec76de20ae82f9b50b9a45517d8e9f810 \ + --hash=sha256:a5716a33bfabb2c6ce27b6cf03253467b3804f83e215f4d202685cf93c6c9874 \ + --hash=sha256:a5a00665d1a0e26763a7338d7e911d4598fbc1d50dd0d6b7919b7dc6c5d6569f \ + --hash=sha256:a5ca5aebae78a0bc13c1943af4af615d4966c5b650b05d5aa83b50e427196fee \ + --hash=sha256:a7b85b2cc6ea45e5f7e8c9a30bc9fabd47cda09106cbb4b967335c3e6c43b69d \ + --hash=sha256:a83ee7107df13abe42a54a6654670eef9bb39425cf2e27f65e0007465e1286ab \ + --hash=sha256:aa7d00b1700966d2917e54d278aba86897890ca9276dd8b76cf6446b6c181b92 \ + --hash=sha256:ab620eb663952455271ac37f9aaad86b73c969c02f11f53cea405b38e96a4300 \ + --hash=sha256:ad8b9671348d7c8716715652ae11f85ed0eb99e265a2df2ca490577d69860b2c \ + --hash=sha256:aefe930d113798330e9462f7874542977869c0613cba3262e2de3a8d5dee8f3a \ + --hash=sha256:b03af77d77e50edba2030fd5f7c352ff209314b09030a3cba7c14edf9a09a444 \ + --hash=sha256:b390aec180a7c054919c04898835e1c77bced23ea8383eb2c570213bf25d1a86 \ + --hash=sha256:b3d78f7bb2b9d9a30345be1474b9aaa8685430b54afb51ba3639b5c6c11e9ed6 \ + --hash=sha256:b5664603a253efd3a75716d793d1d3a6a82723b61dc6db767b2460bbbeec4c0f \ + --hash=sha256:b69602970994a2ed8bbfa78c2f0394a7435226c6040489702d9f0a0ad0c07052 \ + --hash=sha256:b6ae6a0328f0bc035741820fdeecdcd67bf4694eee03972e843663107122f450 \ + --hash=sha256:bad20d4c69c851c982a1e3606f4c293edfd5a87885786c50082412240c4b1ffd \ + --hash=sha256:bb7c99f0673c03017a3ee01e54a5c2617a05468b11eabe513b0080e063ed95b1 \ + --hash=sha256:bebb89489b279b2f5661bbbb2abcc87bcd4a46607bb4a5c966f04f1db6b8df9a \ + --hash=sha256:bfd1de989b3330420e29de39352f5c049905c9e3ee67233a50d550e3d652c148 \ + --hash=sha256:c2306e8bb53601979fcb3fa09cc65e031876d9ae01eff2fcbcd7a84ef94d5bc1 \ + --hash=sha256:c3a4e41e3096bf1f0f1b76e2ffd6d828d6547f574f702d59bdbef7acfa59db9c \ + --hash=sha256:c6834b92dd2428e2dd85ef3d85f723d3c12f20aaf43a2ddd4f944ca25d833408 \ + --hash=sha256:c90d3022d8a94778939cda8638c6c8da8fa757b8958dad7ec868ce29c87681b8 \ + --hash=sha256:ca307d6c259e5c98d3cb9ade55342b47a6839762caf2536f3d7b46ee660cc82e \ + --hash=sha256:ca7f6fe0f37ca978a1e5eb7a3a68e6413f417e78e838324947ffd420202b198b \ + --hash=sha256:cb6fae641357ed2f6e533c0d3c6504a4a5703621a50c89459e46051d56b61140 \ + --hash=sha256:cdaeeb6c350106df6bf9d873395973e5f066a9713200b72cd64f55d0a3eafab6 \ + --hash=sha256:cea20da04494e662b83c872683bf4ff2345206043d036315ed0e924b652e7294 \ + --hash=sha256:cea90547bfd93807e0013a004dc76552be44fad3bc1cc2b38610a9e889ed098f \ + --hash=sha256:d09037ca068d784ebc4aec290ef952ca27ac15dd9c0b5801a88c6e1096b83e6b \ + --hash=sha256:d27c2123977cb9269c30a49ba45f03a4323017ef693e19db4ec9dbe1299a3002 \ + --hash=sha256:d50de98e8d807dc31822fff96f50293163a62418eb65487a21b42713d72ed0b7 \ + --hash=sha256:d66a64dd5dec136040ec2ae94aa026a912ee60fdd45bc28d3db30037fd809e88 \ + --hash=sha256:d79308fa689fac89cbcfbd4dbfc80b5f95c54c5a7fd4d194be221f9d33d026e6 \ + --hash=sha256:da3275833be0edbaf4830fae08bae3dc7219f40ce0c37eaa6c25825957e06612 \ + --hash=sha256:dc1a26b8e53395a01c2c611e58602fa47461f136fba7cd5542e6db6d64be1839 \ + --hash=sha256:dc23390afe9f4ef9ac3bcc72a03a56eebbde03f4c571a32cb38f859cff9a6524 \ + --hash=sha256:e05c2f7925f1d88778e53cb44f14e0223204a3bdd09a41664750363acfb1f2ef \ + --hash=sha256:e12dfea7f5fc2a34a9080efbf79c4c44eb380ec5b9c6fea09407e08f0d1e941d \ + --hash=sha256:e4e4523d6f336708d732516e6cfca7796cf3d96c9474eb5aecf6165f2f1fefc3 \ + --hash=sha256:e4e49f7e1a4e7191bdf9dc67a974db714501b1fc52c24324103d06a86abd5c08 \ + --hash=sha256:e68e151428b5384f766cd25739bf77c7e4a3dc93b5ded7a12118d9fbfdf78ab6 \ + --hash=sha256:e8e4d953faaded9ec7ede36824e9814082d22d4c7b1eafbfa079ecba8cd0d076 \ + --hash=sha256:ee9df1f0d77b9c6e94f4ac0fec533fbddd5ea3a327807f18d7b069ae019ded80 \ + --hash=sha256:f0a887b6565bbfe80efde2b7f6e8890d7d9bbdb11bdb17028a3690c32fe0621f \ + --hash=sha256:f0f4a42db92d6ec7677ab9d12830a2a8ec145a9c6d15db2b593466bc875c78d7 \ + --hash=sha256:f1303ef2eec81262a4b708c3e858afe58d7c75ad91c1c05266eda7673369859a \ + --hash=sha256:f1d56ec54d257d05e0b50f5780d967540cd07beeaf9e5f645b26d50cce79f4d8 \ + --hash=sha256:f4167e87b397f273dc2356fcf1eaf50a6bac51e6105f45103ef7129c8efb0255 \ + --hash=sha256:f76fc85bd054c806960f917ec0f329e24e436f1712267d90588e4c39890caa63 \ + --hash=sha256:f942903fde7363d1d879057ec5de01310efda2597161784d752fa9953a01a71a \ + --hash=sha256:f9b1c4900736e489a812c529100de4b8fb617d4db075e931e213c57424b83d9b \ + --hash=sha256:fc271a6f0a2126958f4090e5507b9da5848927dae331f8f763bd4aa642b3d2cd \ + --hash=sha256:febcce10f2bcdbb80b4ea919238a6a4ac13dbc4c7cadbe8d5d75c3682f8b5404 + # via matplotlib +lifelines==0.30.0 \ + --hash=sha256:ac7c602c8aceced9770d3977817c9d99c250ed8cd86f2567fa0d23e4e8014bf9 \ + --hash=sha256:f7f6f6275fcb167fe0f5b1ef98f868993f9c074cb74b1dd6e92736efa854be18 + # via ngboost +lightgbm==4.7.0 \ + --hash=sha256:129535462686f274df179133643118c5c5c5667167fe6c3a28d955f0b3c8e868 \ + --hash=sha256:d23e922acd891e77212e4d0fbcee9ba973c96dee479491341d05ba595357ebb7 \ + --hash=sha256:d4529acec5c6fefe4768302a529707d0ead90f6a6f42df694b856212e09695b8 \ + --hash=sha256:dfc1cfe8e760387be1e7ba7a214688be21fdff96e4ed9749188f83e1877c2477 \ + --hash=sha256:f42d1e5b32b6f170e606d7c689c6165671da98d7bf37f1addec2623efc8740c9 \ + --hash=sha256:f8e20f682c9aabd000bcf4a7ed8aa6f473c1adfecccae34ec24e823d156f4af0 + # via -r benchmarks/v1/requirements-cpu.in +llvmlite==0.49.0 \ + --hash=sha256:00f16db782f4a13c78c5804aedc434e46794a77e89999a168f9401106270e50a \ + --hash=sha256:039fa4054a06f537fb39248d4472284ca96be311a142ec09e69f95630ab469cc \ + --hash=sha256:20496a5c9fdb8179fb9300e7d19f6782555d98aeeb4a322264aa7fd99f980618 \ + --hash=sha256:294e2f0b70aef8f92d0ae7b203e2609f08beb39437eee73de59a21669331aae9 \ + --hash=sha256:3a9c9e3af4e214acfefa4f73ebe7bc3fb35854a62b654edb3953f5ae33c08ba3 \ + --hash=sha256:4281a0171d66d2098adce4ba706b8c550b1b10718650f682d64cde16e84e4de5 \ + --hash=sha256:4b0e710880b7cc910392bd6b9f1bbf468fed99b182e4420d51598f36114b3dce \ + --hash=sha256:4ec8ad805e7515cb8440a690eb3cef4d34acb29eef80b705ec4e1c1ad3c43c68 \ + --hash=sha256:6a5b06c1b5fc4ae4c9b169b065f42b719448ef1f873687ef224ef69969b75ec3 \ + --hash=sha256:6acba646d88abbc87d5c113a3d62c1fbf8b8fee11c6493f516803e30f21ae870 \ + --hash=sha256:80a84683d04516bb51da1bbeebddaf2c2f558809c93078a8f91807909ae331f8 \ + --hash=sha256:854941c2267fd4fc5b2ce02b8af8ecdffa79fb7784591d3a89370322039ea09f \ + --hash=sha256:95d1071023ed858b79f6971954fd7cc1f5dbcbab987718a4ccbe1411e47d0b81 \ + --hash=sha256:a1b414dc6b164738ec39dd8987cea73829057b7dd92fc6d91b52838385fc1dd2 \ + --hash=sha256:a8c0fc9d624bdc30a3d2db11eb2fb98f80fb209d20b37604eda516cd9b699cf4 \ + --hash=sha256:b095f15fb12c4d90495df5b1a3772b4732cc408398b204a787dbedd370e09c69 \ + --hash=sha256:b352c14353330c879e339b8f8d7491d565fe94242697714a24e80bd757202384 \ + --hash=sha256:b541c8fac3450db7574d1f53cf9dff83f285bfed9d69bf81fe71fc2a7d4f97fe \ + --hash=sha256:be637e465010bc9c50f070468f7f1cf5385e92fee364d192dd5e6cea790ecba9 \ + --hash=sha256:d3dee64784201b64c13a8df62c48a4f4218858faaa65889866bb29bdc243c038 \ + --hash=sha256:d5555ea1d63928481cbf7fcb1d67452b216c7e5b393a4eb7aa1401e67f2a4fc4 \ + --hash=sha256:da7b64474ac15ca595efa2644d5c6836638ccf70709fad3aba3fc56a55966928 \ + --hash=sha256:ddc7aecd4f56397ed6e8f120ec5dcd5a1a8f0e6032ca4af413462792d4dca2e3 \ + --hash=sha256:e32adb84fdaae28aeb86fdb6253084ee707ee157289a2e98fe3caf48a62bee82 \ + --hash=sha256:ee81e96c15a6f870918f1eb60c913551c16aa23defb4f5f1acfa660d6a0aaac2 \ + --hash=sha256:f3f2ff0aeb17d34fcce9f79b99baac441cfd3efa41b83e233ca4530a72381f72 + # via numba +matplotlib==3.11.1 \ + --hash=sha256:0c1f44890d435c1b4ef52f701ad5828cb450ea97bcc83918fda6be74965d6cd2 \ + --hash=sha256:11664c551345553db92e61cae6cf1376f138f8c47cafdf13b64b18f3e3e9e464 \ + --hash=sha256:1524e2bdd48a93557aa47ddcfe9c225dfdd57d5a01a5c49128c20f0632980ee1 \ + --hash=sha256:191163532cdefcb1571ca38a6d7e6474baccde64495783e6ba47aa07ec4b9bbb \ + --hash=sha256:1ac697e591c11b6ad04679a73c2d2f9980fe9d9f0311fb414a2e329706343dfb \ + --hash=sha256:216fbb93a74add02ddb4cb38ef5348f59ac00b3e84567eaf16598772d40e150a \ + --hash=sha256:21a67b961a6d597bca54fae826cd20695ba4a6e4d05424a08da6e13e3176fd6b \ + --hash=sha256:2abdee5ffa2fe11b2d19f7a5c63b785fb7c28cc46c7bc1814156341d9d1a33e1 \ + --hash=sha256:30c492d4ba9448595b6fd8708c6725963f8148e25c0d8842948da5b05f0ee8d3 \ + --hash=sha256:3d3fd84082b1afbd9398466c81309e20045be20d48fe0fb18c43504d164cbbb2 \ + --hash=sha256:427258425f9a3fc4ed79a91f9e9b9aaf5a82cb6571e85dc14063cc6fbb993741 \ + --hash=sha256:480194afceca4df2f137c2721227d3cba67121fbf4397b69cee7f83714b0a58a \ + --hash=sha256:54d47b8ae8b579633a3902ca5b4ad6c1e132a5626d64447b2e22a66394e79987 \ + --hash=sha256:5af0dcda57d471440a7b5b623e70e0a61003518443d9098f211a96ecfbbc25be \ + --hash=sha256:5e1f8922ba31959cf6a9dfb51be64b7f7bc582801a3957dc0c2f3afcd3537adf \ + --hash=sha256:5e510088c27a89d53580a752f959146893563e63c330e161d159b0fee652af6f \ + --hash=sha256:6771b0cd7838c6a857a7209814158c0ad09bfef878db3033dd82d70ad101f191 \ + --hash=sha256:67e4c3cd578c65ebd81bdc09a1b6592ceafee6dfafe116dc85dfcb647b5bbb18 \ + --hash=sha256:68408341f2312836fbbdf6b3c78047f65b2d8752f5fd221c3e72d348f5b34f8b \ + --hash=sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30 \ + --hash=sha256:6be943cb68bc6660ead58c55b3aa6366cba2ef7feb06460fbcce32360376f19f \ + --hash=sha256:7389b77ed2ab0552f46d9a90b81b7b8e6dfcdc42adc36c37a0865799843e0e3e \ + --hash=sha256:7f33a781e12b1e53b278deb2f5373c2e55ec4f10727be3440c0cfb5cda9f944f \ + --hash=sha256:83235693abde86e5e0129998f80ee39fc7f58e6d56a88fafb28a9278833e9d5f \ + --hash=sha256:88a2a27dd9691ae448dfae4b26f59036be90c3c28757edd3553a29559d00859f \ + --hash=sha256:89b193b255f4f6f7948dbcee3691f4f341ab05d9a8874a67b45ddb4182922eda \ + --hash=sha256:8b14eb22961fe865efb0e4ff167e333e428908b00115a8d800ccb65ee108e481 \ + --hash=sha256:9601a1e90be21e4884c53b4f3dc3ee0544654946f9975258d691f1c2e2f119c6 \ + --hash=sha256:96f4bdeea33a8d15a071dbfe6d119451b1d719c733ac666d65357082901a9099 \ + --hash=sha256:9a076f4fc5cdc43fdf510f5981418d25c2db4973418d9f22d8bb3dc8045ada78 \ + --hash=sha256:9fdf1c818ab05d0e74002091ddaf414478a3a449ec9d51c8976d45be7e3a01e2 \ + --hash=sha256:ac104be2768ffdd8655db9e71b768cbb45f2b9aa7b450cf1595e8f65d3822319 \ + --hash=sha256:ae30c6109848ac0f9fa36c5d6270938487614c47ba31860bd5361266dabc5685 \ + --hash=sha256:aee55e9041211bf84302ab55ec3965df18dd90ae19f8b58332a7feaf208bfe83 \ + --hash=sha256:b0a19dcf73406d3746d25a5ed42d713604c9a3e024d129b102852b0d941cb9f3 \ + --hash=sha256:b4c78ceb2f11bcac7389d305cda17aeb1f4586a857854ab5780bd3dd8dbfc407 \ + --hash=sha256:b7cf158e7add54a8d51ac9b5a84abd6d4e13ed4951b4f25f1c5139f41c2addb2 \ + --hash=sha256:b937b9dba5f5f6c1e31c47abe2186c865c0914fd18f2ce0dfc39c9adcef5951d \ + --hash=sha256:ba8f811b8ddfac493734d6af0b2dff96919d0c28ca0d641858dab4262777c6ea \ + --hash=sha256:c52f7ad20ef476806ed212380b1d54d20310c8b86bdc2c9a68b51f0024a44472 \ + --hash=sha256:c90be0b73568da4f662afac580956a76e308437e641b4a45aa08925eeb67d95f \ + --hash=sha256:d2ace7273b9a5061a3b420918a16fae1f2dc5dfee1abcc13aba71b5d94b1820c \ + --hash=sha256:dadfe80797174e2984aae3be0b77594a3c72d2c0a40fbd4a0de48d2728caf3ae \ + --hash=sha256:e15ef41507f3d525f46154ac9e3ae785dacde9f20e593a25de8986267892ef74 \ + --hash=sha256:e4b9ac2f1f607ecda2af90a5232beee2af7582fce1cc30c4b6a1b012dc21ee99 \ + --hash=sha256:f2912f647f3fbe1ccf085f91e213936f9101bead81a5e670565b1f1b3712f4fb + # via + # catboost + # lifelines + # ngboost +ml-dtypes==0.6.0 \ + --hash=sha256:008382aeab529df5d3f00501ad9a7dcd64494d4b5b1971fc4c79019e6c1f5010 \ + --hash=sha256:03ce583adfce34ad33aa9e1fc7a8344dcf90ea776cc4ef0e5a48d4eae84e5d20 \ + --hash=sha256:084dfe51a7ad58b171f05115f8226ed4233a454a1611371947e806e76f0c638d \ + --hash=sha256:26b1f1fa4f0435a2946859823f6e2bf06796f1e9f10f5a05b08a5e3c8f46ff69 \ + --hash=sha256:28d676428b104bb9717b0928bc5c5129f2d6b51b6727587cc4289e7bf8713cb5 \ + --hash=sha256:2a3e9d53925597fbffafd2a37048dadeddd0bdaba58058f6ae0869ed709a184d \ + --hash=sha256:3035518e3e19add1a4cac9236ab22888b208a4074912514313ccb2d6d242cde8 \ + --hash=sha256:317be9967fb84b0ce4e80e6b1bf71213d21971621cf6f1e501a63602a95297bf \ + --hash=sha256:31f1ce979d31a357e95aa81812f20412c8c954fa43c44ee3ead1e1c8a78575ef \ + --hash=sha256:37da32aa97749251025666d62372775019594577b9c9e9cfda83bed48d778fdb \ + --hash=sha256:3b4a480aa8fd54a1805b8ac10f3f91763926a74f73c0c364c10f9231854f4170 \ + --hash=sha256:3be9911d953f97cddded4b9961d7b650473b7e55806d20f6176f8356dfe7b38e \ + --hash=sha256:3e169214e0d80ff1c038e1b3017e33c23e43bdf948d42d31de8283111c7e2fa3 \ + --hash=sha256:488c99ab181a2f59d9ec3b12c5fa11ec904e92be2c4ba18cded54dd7501208fe \ + --hash=sha256:5359c588cc62de6f78d7430f06b65853d884955494d86d6ad90b6dd64a3f3a08 \ + --hash=sha256:573b11f3c327e17ef3826d266e676cf1149a1f3016f822a05f2306c55d8246bf \ + --hash=sha256:57ed0d6b4ac5e7868361303a9c57fbcf63b768236ee14456f585dfcf260d0292 \ + --hash=sha256:5a519c9e95a216fbcb8e759793ef7fb40793fc803ed839142d6dc5be9be5bc89 \ + --hash=sha256:5e60251d32ced5598972e4d5e06a2f044341f9291402551a3f6f0ec44f9299b0 \ + --hash=sha256:6c8e39b53e90afda8ce52859c93de4dba3e02b76d85dcf091cc469f9184c6dae \ + --hash=sha256:6eaed129a4afe90694b8685e2f9b6294849f5eda4af9a15be83a4326eeebd775 \ + --hash=sha256:6ec0d244a5bba12239025389ad88bbfb45f9f10e25ab4f678e9a4768ebd47532 \ + --hash=sha256:7728c0420ec1c338564fc8b01015ff2d58567e70f17fedce5a0a7c0308c0d5b9 \ + --hash=sha256:84fa136b8602c8c39e3b6cb24918960cd6f36cade7a70376f56770729cd56510 \ + --hash=sha256:8f490c003369ce60e514a0c3b12374f05274c101fee1bead6740ec8a564032b0 \ + --hash=sha256:9c6ad60af4102789a5c09824004beade2f7f28cd1cd581ee5c170d9dc2fbb00e \ + --hash=sha256:b1b503864fada3f74fabf8d9fee7b4c1cbe956301e6fdece975d5f77c2fce958 \ + --hash=sha256:b76fa1d3f92967d58289ac47ab7458ede66e6f3527fff3e59142aee57d9307cd \ + --hash=sha256:bad8d1dd5bed060a29332b99d63d0e5c2969081e1c6ea54adfbccfdfa783be44 \ + --hash=sha256:ce7563e0b1a4482cbc1b4a6272145e54e4489e54fe7428f94908c3d87103abfa \ + --hash=sha256:d4f1b9329a251e4affe3bb58f4d3e2db22a714396fd7ffb40d0b5db423c24d17 \ + --hash=sha256:d574c2b28921dc72e869df248f1a278f6eee176a1f237c8642e1a71eb15f3977 \ + --hash=sha256:de9d14748dbf3968951436ef514a29c9d1fe438aa680d110134ee2f7a9f9df18 \ + --hash=sha256:e25bb3b0ad1217b60626e4ed45b10ca170c41d99fbe44a12bebc1e07ec4aad55 \ + --hash=sha256:e2d6149f3a57f405bcad5fb41e03218b8373936253f23e1ca84c0108abbc3392 \ + --hash=sha256:e74266ca8e97874a937b7646378c178025650a236584f7474d10d8086a6edea3 \ + --hash=sha256:f4adb4af61516510d786cf8c01851a66f6d3ddfa79e1144deaa5b40d8507231e \ + --hash=sha256:f4f59f83c82ab480e924b988e7b1b4eb4de836dfcf5390c6f59148d1a00e1d02 \ + --hash=sha256:f6cb525101b6b903779188c1e9e9490c343b455ab822883e02cf01e5547338d2 \ + --hash=sha256:fb87f46b4f7ad7b5d3ad8f4b452b024bd4229d44c8ff934798c1fe656210387a + # via onnx +mpmath==1.3.0 \ + --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ + --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c + # via sympy +narwhals==2.25.0 \ + --hash=sha256:1f0f403e8c7e4463cde9bfe78b12fdd809e3ae3dda6d9b2f802934fb9c7a6a8f \ + --hash=sha256:62c036c810662bf7820b7737077176313bc59350eeeefb808510f388c743e4b2 + # via + # formulaic + # lightgbm + # plotly +ngboost==0.5.11 \ + --hash=sha256:873326442f00632829a521209b1030e150e0ef0a9de09952e044f8f3b8839940 \ + --hash=sha256:c3683334ab6ad58d79bc50aa11d8f090f4d452010c73dc2f2304affc448b08fa + # via -r benchmarks/v1/requirements-cpu.in +numba==0.67.0 \ + --hash=sha256:00c964a5b94d3ae82d83ac162cd610755875b98dadb779fdde06e6bfcdbca47e \ + --hash=sha256:3fa3d1b27f96f2c0d54513d953d7197886aa1eaa7d2439a0eedc44d993fb181a \ + --hash=sha256:4a2ed006635bbd0fe45681ed49f3b4f4bad1abf0c233bcc5842c9e3a34cabd61 \ + --hash=sha256:4d576e62bf2c9370f61312b51573c4bb1f3fe96798bbab56730847a368a316c4 \ + --hash=sha256:50e2b72406c18cda5dd7431b0082cb85ea94e06c64c33607248fc8bef92cfb81 \ + --hash=sha256:5269245a675abdd3e2c35ec6bb2f250355effa9032514d8f2354f0d2d10854bd \ + --hash=sha256:6004d8d5f28d4028687fb2d972d629295b13685943bd2ed5cd8810c3b848e219 \ + --hash=sha256:694c81c6560b2b47e5fc1dc39c29175b907adf862d9af0af801453400a022a61 \ + --hash=sha256:76d3335aaeffb9dc88309420890e73497a00be08a7530441bc2b58ffe025bfa5 \ + --hash=sha256:77e1c7173fee57a0d84e006c7e70346689d6cb3e7db503489bae58646b4eff7b \ + --hash=sha256:7930748ce8355d2a5a28602abab056a61fdc676d17377f27d17993905428171f \ + --hash=sha256:83ab968b0e0fa744eba03351282dd8000796e6ec8e4518f47bd3ed86c0a20c7b \ + --hash=sha256:88f6e0f5cb6c545e158b6ef0496c01b6d6958a7ccc6634a1576a94bbbab29ff2 \ + --hash=sha256:8c0e88acd4341ddf40779db3c0228b9188aca7fcab5f5f3ce9949a1fc71e9a02 \ + --hash=sha256:8c80c847301dc33dc8f84a97a952004023d9a05578ae4512b087176264cc1960 \ + --hash=sha256:9c4953387c77864b596d8296e2cfbdef82b0eea4166ab4864b05d226c51143e0 \ + --hash=sha256:aa5f002f665bec321b950dacaa26ee009e1d720f6ac9d9856eed5efe1caa03a6 \ + --hash=sha256:b68ad5125fe245339cc8dcc036081fc1ea482c5063387b9612a76ccd83dc91cd \ + --hash=sha256:cd75aa535b33fa05d9d930b1ae8af9f97a2881e96d72dfb38ec9b78284d9f851 \ + --hash=sha256:cfba1ac34f0363fb1a250a10e97240780d11e05227892f7286b26fbfd0ad58ce \ + --hash=sha256:d6c8e9ba3f9602471e8c6f563ffcce8db8046741f0bafb782a052e41dc6b6861 \ + --hash=sha256:e7a7b0121466f1e9a8a074b0545fe90e16389623abf979b5d7c299dca1294d7e \ + --hash=sha256:ed333e0af4386294e7f03e550e01411856b6935e717d859225e0a7338c6b6795 \ + --hash=sha256:f074a8e23db78490f11a3930c940be758316c10ac5985be83d2f298dc080acf7 \ + --hash=sha256:f63d43db06b4756424d6d2484737c902e0ae944a0eec3e8b0b4de2c695b15caa \ + --hash=sha256:f99f880ff25f418a67f9a1d00d0ddfbc63430f627b523e515085a592a7567f4b + # via py-boost +numpy==2.3.5 \ + --hash=sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b \ + --hash=sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae \ + --hash=sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3 \ + --hash=sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0 \ + --hash=sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b \ + --hash=sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa \ + --hash=sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28 \ + --hash=sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e \ + --hash=sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017 \ + --hash=sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41 \ + --hash=sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e \ + --hash=sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63 \ + --hash=sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9 \ + --hash=sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8 \ + --hash=sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff \ + --hash=sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7 \ + --hash=sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139 \ + --hash=sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4 \ + --hash=sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748 \ + --hash=sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952 \ + --hash=sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd \ + --hash=sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b \ + --hash=sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce \ + --hash=sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f \ + --hash=sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5 \ + --hash=sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42 \ + --hash=sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7 \ + --hash=sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248 \ + --hash=sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e \ + --hash=sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3 \ + --hash=sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b \ + --hash=sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e \ + --hash=sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0 \ + --hash=sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa \ + --hash=sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a \ + --hash=sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5 \ + --hash=sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d \ + --hash=sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4 \ + --hash=sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c \ + --hash=sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52 \ + --hash=sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5 \ + --hash=sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d \ + --hash=sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1 \ + --hash=sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c \ + --hash=sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18 \ + --hash=sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7 \ + --hash=sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188 \ + --hash=sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218 \ + --hash=sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2 \ + --hash=sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903 \ + --hash=sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c \ + --hash=sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c \ + --hash=sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234 \ + --hash=sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82 \ + --hash=sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39 \ + --hash=sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf \ + --hash=sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20 \ + --hash=sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946 \ + --hash=sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0 \ + --hash=sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9 \ + --hash=sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff \ + --hash=sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad \ + --hash=sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227 \ + --hash=sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10 \ + --hash=sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e \ + --hash=sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf \ + --hash=sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769 \ + --hash=sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310 \ + --hash=sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425 \ + --hash=sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013 \ + --hash=sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c \ + --hash=sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb \ + --hash=sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d \ + --hash=sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520 + # via + # -r benchmarks/v1/requirements-cpu.in + # autograd + # catboost + # contourpy + # cupy-cuda12x + # formulaic + # lifelines + # lightgbm + # matplotlib + # ml-dtypes + # ngboost + # numba + # onnx + # onnxruntime + # pandas + # py-boost + # scikit-learn + # scipy + # treelite + # treelite-runtime + # xgboost +nvidia-nccl-cu13==2.31.2 \ + --hash=sha256:0bcaf0308854cb55fcc35af72e2c83143f3b71e65a4e865e2c586b1cdcdb5ae0 \ + --hash=sha256:b5563f8e2534f363d93ace022670ba016d3717e190ac4eba564d05fbbe8495b1 + # via xgboost +onnx==1.22.0 \ + --hash=sha256:19e45e4af88e3fe3261458d4b8cc461957ae2782a358a3560503569bf3b23b72 \ + --hash=sha256:1d0a2bdb15eb2b3cb65c438f3423d9620d14fdce32f92380e6bb1b2e09568ef5 \ + --hash=sha256:239958534464612fbcb6ed23d5228aaa925b39b8773f58726809ffdccb4edd1c \ + --hash=sha256:2632406b8f523ef2e2873c363f90b20a3d88c0fbcfac757d3addffccf8f452c2 \ + --hash=sha256:2d8f229a553fa440fe623ed7b36fca5e7762da3af871c3f8f8ce451df73e2914 \ + --hash=sha256:33ce94119bbb7f05d9caea4ea7549f5185a54369f6bbc9f70171bd5ee6935bbc \ + --hash=sha256:596fbf0490947533c1c1045ba860851dc9fb77471023dac9a71ba5b42ceab103 \ + --hash=sha256:5c1c0408a9d4b4df33851672e5fc7590b96301ee123396d608f9ab6f045ab06b \ + --hash=sha256:6d0ffffd63a4ecc21ddaeddd5bf02099cb701aa4243f2de00122726869065ca4 \ + --hash=sha256:72ccebab3bac07215c204ce8848d42e78eaaa666badbf72d25cd359b9f269e3a \ + --hash=sha256:82e9f27fc1223cb06d68a56bed6f9d3caf3d0dad1b61bce45006d529b15bd94c \ + --hash=sha256:8561a2c00041c07e08db0c228593b5b4694100398685f348532af7dbb84189da \ + --hash=sha256:87a3077958f66f9a26dec10077ac28326d9cec2cbe1f0b040947243449754573 \ + --hash=sha256:8907b9b9389893bc0dc6314cc00ee1e3a69844e48d689eacc6a0340411a7da58 \ + --hash=sha256:8a5eccce2d5fc6c5046928a9aa7cdd9750ea4a586f8de341d3d40d820c35fdec \ + --hash=sha256:8e268cdc0547e3949799ffd4a44451dc2b9080b57d0824a2db680b6ec65506f0 \ + --hash=sha256:955e02e1f6d385b53d52f9cd7b9cdf5caf417c300bcfe3c64c6d542be763845b \ + --hash=sha256:a1a89a7cb9ba13d78f009bdec448ec82a98972589734f157022a2bff7a5973a6 \ + --hash=sha256:a3a39fc4643867aecb33417fdddb11e308ee79d2d4a584b9d50cc7aec2091b13 \ + --hash=sha256:ae5a563f281cd9d2845622cecf6c092a57e4ee1b138f66fdbbdd4200567a5e16 \ + --hash=sha256:c21a0e59fd967a95b358e4a6e756d1f1eec2d304a83480f329f66e30d2bf0223 \ + --hash=sha256:cc8b66b312f8f03a53e268afb67180a2d97dd12cc79e2b61361c6c0073448016 \ + --hash=sha256:ef40c0aaf0b643857ea9306fc7eddce17eaf9fb0407e4801f1fc5758443a38e0 \ + --hash=sha256:f3c120dcdb70ad738f3c061b32798f408ea299eb69f84dd69ab4a6bf3c2ec01f + # via py-boost +onnxruntime==1.29.0 \ + --hash=sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f \ + --hash=sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98 \ + --hash=sha256:11264bb58f7b7cf6af835ab10d36838d73680580820fd6f51d90124a1ca8f449 \ + --hash=sha256:16925ef8497e2c07e4b5ae15b504079b3ab3f65e22c58efd10dde0f3caea969a \ + --hash=sha256:1ea91cef3b971506e51ae9c37c16d027774ec64994a524ec1bdfb027d68a9832 \ + --hash=sha256:2945e1f82f81f27e88decea88c7861f45baea23818950d467bf3909aa303119e \ + --hash=sha256:2b80d8c7ec2cc7438e4da3760b88c24568cba72c9ace96d668800a6c79419acb \ + --hash=sha256:3a3814c041251d6a77fdf513fb282056538ee826d2f1178a0df3c549d3fff6ba \ + --hash=sha256:4a3129ae56e70d2618ff773920166916310370a7e3cacb60b9e0e8910092725f \ + --hash=sha256:4acf2b4948b7ede87221ca6332344b8facdc8059d6ac751a7d367d04532b02dd \ + --hash=sha256:4b940b0d777590c7e20bf298f5c16af1ea6ad1b400a1c822a6be192f64f4d954 \ + --hash=sha256:4eae472cf7dc3107dec1bb53cd6d142d1964616d08aae48654cd4254b2363c4b \ + --hash=sha256:533f8370ce124304e5cb08ab961836cf755631e3dd77adc5f3bbdab70c2b7d99 \ + --hash=sha256:6c0c37b92f67ed68dd36221ce0403e1d9bd4f7efce724439978a2597848530e5 \ + --hash=sha256:85f8e8406c52658735fe5c7fbfd3ebaa1ed340768324f6252e4274e374580a23 \ + --hash=sha256:939e5d65f332e6d399774b2bd0d3559fd8fa629c1e77833db29d968d2384f23d \ + --hash=sha256:be0f8ed688cfb1d4d5765a137193b7bfab0c8ea214eed99260b380bb525a3a7f \ + --hash=sha256:c1ad3f437153fe77f9d01a08fbaac0beb030e09b8a80ace1603bcf69b6c95481 \ + --hash=sha256:d2fb19e848f7c33ed8d3182b52504aaa11c5e8da438bbb47296f85b133cbcf6b \ + --hash=sha256:d67673c5367727860922c5262d724472f1b5539fb7ccf4c81a638f9b71719803 \ + --hash=sha256:dc61a79cb39afd66ab3f01fd2c23591a7f01de89c1668e1fb6315067fc279164 \ + --hash=sha256:e2128f31f449e922c62dbe5d8b6b7b079f0bcaf2d56a102fa203cb6e5bb5ab19 \ + --hash=sha256:e417ef8628dcce310d2d53023e750ea298ec14d4341ae6dc3a572bfd9bc7fa97 \ + --hash=sha256:e74b278af1d949876f5d91d1268fd6c680e79f2bac194967394eaba9fdf69e7e + # via py-boost +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c + # via + # matplotlib + # onnxruntime + # plotly + # treelite +pandas==3.0.5 \ + --hash=sha256:08d24fe11a17dc33bd6e937dc9c665f9cba08fbdc9f657f405713515febe300d \ + --hash=sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6 \ + --hash=sha256:0fac0010c75e4efb6b99e249c183a8993ce0dc95c240f9b120a5e67c727b7928 \ + --hash=sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea \ + --hash=sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49 \ + --hash=sha256:2946e77e4a53cd248cbde631a12f0e51c8324ce354c3eba4d20147c1ad6f4282 \ + --hash=sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6 \ + --hash=sha256:2c0cf1dd9b55a22d105fc46c1b489af3bd42264fcba7c66297bf47a9a1d9c78a \ + --hash=sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36 \ + --hash=sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca \ + --hash=sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da \ + --hash=sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c \ + --hash=sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da \ + --hash=sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be \ + --hash=sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85 \ + --hash=sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c \ + --hash=sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e \ + --hash=sha256:679f4e85b30ddb1515458ab1e788d3e260eae369b1f78da7a3aa4cac8ebf4a2a \ + --hash=sha256:71ecc8fb7ed1a7aa4392316b5309a6347e8e7f832f38fd897846b3a1457a9298 \ + --hash=sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc \ + --hash=sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41 \ + --hash=sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0 \ + --hash=sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b \ + --hash=sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7 \ + --hash=sha256:b1261758dfb6cf12c3cff8300e21cefad30e7ec709abb4c24ac7318e6a52462a \ + --hash=sha256:b173f5951ff6b8b0ec7675e20dff3c97b7e7a57dfcce387c2d7c5afe87cb7899 \ + --hash=sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce \ + --hash=sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c \ + --hash=sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3 \ + --hash=sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b \ + --hash=sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc \ + --hash=sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b \ + --hash=sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a \ + --hash=sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92 \ + --hash=sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58 \ + --hash=sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34 \ + --hash=sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee \ + --hash=sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712 \ + --hash=sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd \ + --hash=sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94 \ + --hash=sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040 \ + --hash=sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d + # via + # catboost + # formulaic + # lifelines + # py-boost +pillow==12.3.0 \ + --hash=sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756 \ + --hash=sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a \ + --hash=sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59 \ + --hash=sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45 \ + --hash=sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 \ + --hash=sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df \ + --hash=sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139 \ + --hash=sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b \ + --hash=sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39 \ + --hash=sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e \ + --hash=sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8 \ + --hash=sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1 \ + --hash=sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8 \ + --hash=sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89 \ + --hash=sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5 \ + --hash=sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130 \ + --hash=sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd \ + --hash=sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d \ + --hash=sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b \ + --hash=sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed \ + --hash=sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace \ + --hash=sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb \ + --hash=sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931 \ + --hash=sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510 \ + --hash=sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6 \ + --hash=sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1 \ + --hash=sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce \ + --hash=sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385 \ + --hash=sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e \ + --hash=sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c \ + --hash=sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7 \ + --hash=sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace \ + --hash=sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c \ + --hash=sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f \ + --hash=sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64 \ + --hash=sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f \ + --hash=sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a \ + --hash=sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827 \ + --hash=sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17 \ + --hash=sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4 \ + --hash=sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a \ + --hash=sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701 \ + --hash=sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e \ + --hash=sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91 \ + --hash=sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66 \ + --hash=sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468 \ + --hash=sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217 \ + --hash=sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658 \ + --hash=sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418 \ + --hash=sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a \ + --hash=sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c \ + --hash=sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330 \ + --hash=sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402 \ + --hash=sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09 \ + --hash=sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930 \ + --hash=sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f \ + --hash=sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec \ + --hash=sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a \ + --hash=sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94 \ + --hash=sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468 \ + --hash=sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b \ + --hash=sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965 \ + --hash=sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8 \ + --hash=sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd \ + --hash=sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 \ + --hash=sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c \ + --hash=sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777 \ + --hash=sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 \ + --hash=sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9 \ + --hash=sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f \ + --hash=sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f \ + --hash=sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0 \ + --hash=sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c \ + --hash=sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71 \ + --hash=sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3 \ + --hash=sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838 \ + --hash=sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf \ + --hash=sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321 \ + --hash=sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26 \ + --hash=sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec \ + --hash=sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9 \ + --hash=sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65 \ + --hash=sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5 \ + --hash=sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e \ + --hash=sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d \ + --hash=sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198 \ + --hash=sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7 + # via matplotlib +plotly==7.0.0 \ + --hash=sha256:08b21f1244a97e7a1a699833c4bb2678475aa108b3f1989886ed0b038ebfd849 \ + --hash=sha256:78cbf7bd06d1b05bb3b8ec1b709864695229b55151b6f7530fbf55517ead6fdd + # via catboost +protobuf==7.36.1 \ + --hash=sha256:0b53ce95272aad50ad25d7ff03373743209822e8ba42ea7fad27d2bee1547d00 \ + --hash=sha256:39c518c05586c016d7874ff6079ee115bcec1ea5fbb1d177fbf7867ef4c67e44 \ + --hash=sha256:3cf2ee25d006cee57294a1196ea43b37feb78e0dcd1e8af5c1aeddb777655aca \ + --hash=sha256:43d3d37b1eb24c113b9b7d02008cac44e423f00b611b7781ae998d7623972969 \ + --hash=sha256:51139351435d9b43d88a55eaa49fb6f737fbb478fb0cbf2cf694d1a04a9d3363 \ + --hash=sha256:7d951e46b3f963d6c264c367c437921de9d5aedd9c3f9612b9077736b4e3ad5c \ + --hash=sha256:97198b77e369a0abd8e262b8f6c7266c55ddb796a3a12c76d7b8881188ed83aa \ + --hash=sha256:d0f6470f0ce2b84e3feaea2d4b816378b37ba4d4aa08a274305373de93e2d524 + # via + # onnx + # onnxruntime +py-boost==0.5.2 \ + --hash=sha256:16cdfd12c499586cb27dd367b2a4b176cf0cc0f8f5cfbf9f422a83c125fa6a64 \ + --hash=sha256:9e981da7d28c3849daff66cffed3c301b456d3f967244c303ed581a82cfe39bd + # via -r benchmarks/v1/requirements-cuda.in +pyparsing==3.3.2 \ + --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ + --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc + # via matplotlib +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via + # matplotlib + # pandas +scikit-learn==1.8.0 \ + --hash=sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2 \ + --hash=sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a \ + --hash=sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da \ + --hash=sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9 \ + --hash=sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961 \ + --hash=sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6 \ + --hash=sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271 \ + --hash=sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809 \ + --hash=sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242 \ + --hash=sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4 \ + --hash=sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7 \ + --hash=sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76 \ + --hash=sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6 \ + --hash=sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b \ + --hash=sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e \ + --hash=sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7 \ + --hash=sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e \ + --hash=sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57 \ + --hash=sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735 \ + --hash=sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb \ + --hash=sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb \ + --hash=sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e \ + --hash=sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd \ + --hash=sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a \ + --hash=sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9 \ + --hash=sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1 \ + --hash=sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde \ + --hash=sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3 \ + --hash=sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f \ + --hash=sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b \ + --hash=sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3 \ + --hash=sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e \ + --hash=sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702 \ + --hash=sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c \ + --hash=sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1 \ + --hash=sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4 \ + --hash=sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd + # via + # -r benchmarks/v1/requirements-cpu.in + # ngboost + # py-boost +scipy==1.16.3 \ + --hash=sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2 \ + --hash=sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb \ + --hash=sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a \ + --hash=sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203 \ + --hash=sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304 \ + --hash=sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959 \ + --hash=sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a \ + --hash=sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d \ + --hash=sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe \ + --hash=sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562 \ + --hash=sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb \ + --hash=sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9 \ + --hash=sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc \ + --hash=sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d \ + --hash=sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686 \ + --hash=sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97 \ + --hash=sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353 \ + --hash=sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2 \ + --hash=sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876 \ + --hash=sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78 \ + --hash=sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc \ + --hash=sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119 \ + --hash=sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9 \ + --hash=sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f \ + --hash=sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135 \ + --hash=sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234 \ + --hash=sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1 \ + --hash=sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88 \ + --hash=sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc \ + --hash=sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6 \ + --hash=sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6 \ + --hash=sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22 \ + --hash=sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511 \ + --hash=sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079 \ + --hash=sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184 \ + --hash=sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c \ + --hash=sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40 \ + --hash=sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2 \ + --hash=sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0 \ + --hash=sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e \ + --hash=sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800 \ + --hash=sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4 \ + --hash=sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657 \ + --hash=sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005 \ + --hash=sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70 \ + --hash=sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07 \ + --hash=sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e \ + --hash=sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c \ + --hash=sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc \ + --hash=sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7 \ + --hash=sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146 \ + --hash=sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733 \ + --hash=sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c \ + --hash=sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d \ + --hash=sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6 \ + --hash=sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d \ + --hash=sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26 \ + --hash=sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8 \ + --hash=sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa \ + --hash=sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b \ + --hash=sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d + # via + # -r benchmarks/v1/requirements-cpu.in + # autograd-gamma + # catboost + # formulaic + # lifelines + # lightgbm + # ngboost + # scikit-learn + # treelite + # treelite-runtime + # xgboost +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # catboost + # python-dateutil +sympy==1.14.0 \ + --hash=sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517 \ + --hash=sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5 + # via ngboost +threadpoolctl==3.6.0 \ + --hash=sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb \ + --hash=sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e + # via scikit-learn +tqdm==4.70.0 \ + --hash=sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220 \ + --hash=sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953 + # via + # ngboost + # py-boost +treelite==3.9.1 \ + --hash=sha256:05b9e95a3635291f536b3cf550b9cf71aeacd51a6f3589169260d121d5f2682e \ + --hash=sha256:461b7c7980a6bc03fa01573e4fa4d35bde7c4b8c682d264bc90a7a2904d3f6ad \ + --hash=sha256:59be8ca302ee9374a8dc6d788a7b23987c151bff9cfd8f87b6c933d18789e3c8 \ + --hash=sha256:7d51876cd53d68746c6277c5fb933ee32293760f994709dbdce1f583cdde864f \ + --hash=sha256:ff004f7c28517c7dc1fbe70d40d4836428a75595cbdd738a3412f0af2f22c981 + # via py-boost +treelite-runtime==3.9.1 \ + --hash=sha256:2061fd2148a50249291961fc0bd9dd40ffb0300d0286e3def8a9781f13b05820 \ + --hash=sha256:51375092417d43e58524cd2de774640fc62c50584bbc7f80aba53397ccc654aa \ + --hash=sha256:60e6593de0e6befef63652047403c3aee8c85f20a8fae682178de22c6bff4907 \ + --hash=sha256:6cba4d928063c08e7c4eccf02cddf102e004d3e5b0a993a97e607e7caca83018 \ + --hash=sha256:71bde8a1ca2d643e0765c60f5fc7c9d301ea7258bbf2476da9272735b1659817 + # via py-boost +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via + # formulaic + # onnx +ujson==6.0.0 \ + --hash=sha256:02148bd4706f42b063bb95f6cc309e16554fb4c250db4683688c0a3eb83048ad \ + --hash=sha256:03a385e523f67dec6d4dad0970f20a080cad045b56d9a3564d07807090a9c106 \ + --hash=sha256:0a4edbeb091b195031a0e96fab005150340e383c095cac6b5c2b7dc8f55040b5 \ + --hash=sha256:0aa247eb50a52bb2190871ca8c2e0a96f8190bfdb1ebd68c70d1bf422f640b73 \ + --hash=sha256:0d6e29b91a0934ed9d22ee48aa91518523cd2ce1c6caee2810b439fb371b8439 \ + --hash=sha256:0dd8981828f6b515ba5e9f2473f433aa59bebe4784182b48695b71af52033b4f \ + --hash=sha256:0e94f0b95459caa6cb5e333baf6763bf1e7a96ea5e4f1ea7fbb0ad88e81a88ab \ + --hash=sha256:0eeef12ef46e129278b50ca4c66c6b35c318f2fd09346bacddf218ed378cc0bb \ + --hash=sha256:0f3eff1f93d9d1f0bd5eee35883b9c71ad9befcfcd0ddc7cd5862c69fba21cf6 \ + --hash=sha256:102ddbb1677540f0cae80cc36f5db9663a626c7b3bf872ed10f10fe72343a3c9 \ + --hash=sha256:1080587042cb19f9cfb08f289498d866ac5f93393b21006321dea331dbf62375 \ + --hash=sha256:108a9f3a635913d38a856e05007afc9b243929938939cd11576a3f5484925145 \ + --hash=sha256:15aa57f6d0dafccd20f282f46f6a8d721d46c73fd9474f5ba996e9adc48d3177 \ + --hash=sha256:1cda9f81e58120675dbaba7b254849ee59698e5dee83c4383a3c1a96ca92a679 \ + --hash=sha256:20eff4f1ea3b970b998bf111036404eb18e976d4919783f793e539370b8627cb \ + --hash=sha256:212191672712e5c40219d568c495a8a0bec526934eb87f16f30da78d962fe5ca \ + --hash=sha256:2145005321a4b175486dd890946b036bb8730e4e8e17744f5abce23ea014e024 \ + --hash=sha256:222389a616f6407eb40e1efa80a35c1ba468903e50a305faf425c26e3c32bdb9 \ + --hash=sha256:22eafdd4f8ee6fe2db0737285c75b15f7486dc53c07b09a4b3699c92c407c3e5 \ + --hash=sha256:28ac884b58c62eacdb6ac67284475b3f19b8160dbacb723956e67a0c11e45014 \ + --hash=sha256:2a09d4ea9ee60c023220195b229ce2688479dbdcf51630acdd54ee75b27c0c00 \ + --hash=sha256:2c5a1b422ebe9919a39c183543dff29edce76bac90080af5ceed51aeb6b60d0d \ + --hash=sha256:2dbe0b6d417b458164ccf1f59e081d6bd65c1fb2f626e0daeb6fb88c436f9643 \ + --hash=sha256:2e36269e715c8deea036d263557042e2598e79d52110233c1a623ed9e7c1cf0a \ + --hash=sha256:2f3c0a77235d7ffcce5c54b872fa25de4f14e6ffc159c62ad93b0a9ca98a1d20 \ + --hash=sha256:34c0403b485d8ddd86bd29d879cc9f72223579b57188b0a2bc07a8b06f8cfbdf \ + --hash=sha256:3b6494d29f7103a97d930cbd25f23fdc4d77e145a931e743660d697a200fd831 \ + --hash=sha256:3bd770b553bebc408b49d6fdb46efb1dc568368d949ac7813a07fcccaea044ae \ + --hash=sha256:3d56d408ccfb9b0e5c2b4ea687396df30ca42ebe2aedac88362069620ce65402 \ + --hash=sha256:455e6ae6c925eca6358110e665a31e5bbcf0a93dfe9822a26b954c9351de2c3f \ + --hash=sha256:4579b8c96824f65888d4a615463c2dc2b7db6c6f0c7f83ece2a58714fd1a8123 \ + --hash=sha256:4a69419253e9367281db03355eb55b5231eef5ff338bb816eb5926ee788faf48 \ + --hash=sha256:5376a8c14d0eaf80789bdb10e21ae12582cdf526eb921a47f57053ef08c63f8c \ + --hash=sha256:54ab6b66fa6f67dfa8234e109df132074e155af3b299ad83aab13ba4b6db9b3f \ + --hash=sha256:5919fe3109a08f8bd682a2ad1cec5cdeff7c1f563b812aba26e86b8b0ab05558 \ + --hash=sha256:593acfa0f36ada24e89c07147441fe364081fa1631db73ee55f40893c196e0b9 \ + --hash=sha256:5b3afbe992e2d1b8c1e4e7a0da2c77da23f29545e5ba695a4a9241702234f20e \ + --hash=sha256:619b2152aa77c57a535e3e7eaf88ec8e25beac6d380378b2ade10362cce50f75 \ + --hash=sha256:63b56e3fcccc339e2c1332e75adc779bd145964e1a47a39a229fa01b2e25618a \ + --hash=sha256:63eefaa34abbe14167493710619b840d3fc167ba86e5fbe0c4a5eb01686aa3a0 \ + --hash=sha256:65bbea52c251b568268b61f9377bee867addc81c9b4c24da277b051ce16f6151 \ + --hash=sha256:65e0e0c21ead4d0087c9c65a82eb2446c4bd51d36388d41035ce773517e7a3bf \ + --hash=sha256:666a91606eeb47c997927ff294f3a9f8f930a02d0d2293ec7b19da5ed688f7ec \ + --hash=sha256:6759d1a9f8aa45dbe2fb3e49ef181e8e6dacca89c595c5ec007ab2b839235117 \ + --hash=sha256:683501475e3dfa935574bfd2b3d26f7393b4a880a745aeab63cc3d013027bba0 \ + --hash=sha256:68d623416ad997666bd8ea899b15554462b6250e803f4ce084c7dfd06a775314 \ + --hash=sha256:7168df25a051fd2a60f8d123b2123b60ead7c1f22cdd467ab7c2bba0fad0aec1 \ + --hash=sha256:7253ae5cac107d2940226a113165738630a98c19cdeaec1e6d6d6c3a7c307b95 \ + --hash=sha256:7a1472649bc9ef3b9ce3ab279e9e812368bfac25210b7ec96bd544767c019577 \ + --hash=sha256:7de7692f330c1ceaf6335ad8039d2fe9344d30ecb415e86ee719e9d5585b2077 \ + --hash=sha256:7e747c535d4ca9afdde31e034484a1020717fb18fa8a8faa789171abeb2ad1ff \ + --hash=sha256:801ff407fda799f4ff98d960342128b065a14113eaccfc116b50092342636861 \ + --hash=sha256:80e23393feb707582e0ad495c397a4477b646d08094d2df64f7316f9fafd8aae \ + --hash=sha256:8141cade37dabc5f090eb5e6a267eabb6b193078becdc82aaf10433196715c33 \ + --hash=sha256:83194e213d9df2f2aed1edb821689f99c0f7789bdee173125fda510282f61070 \ + --hash=sha256:83ed82fe4a17fd30796e65edeb46409f49e2794a33c0b6649d5194347f2412f0 \ + --hash=sha256:8604968307105c3229ce0170e70bf3f172cf96f73c978b1afbc3d0ec8bdfcf86 \ + --hash=sha256:868856ea75794d952c773c506bb638e2a692bc5a8095cefebdcd98f43c79e772 \ + --hash=sha256:88b237680c705fd37bacbaaa335106fecb234a47e1df0737d949b8e32c7eb5f9 \ + --hash=sha256:89b1962c30dc29ba99e522c4f2e39173961b6098328cfbcdad3f9f1c308dae89 \ + --hash=sha256:8af54166141d5c8ebeebc044c3569ef10edfcdf6fd8ecb487a2bf33c776ebc8f \ + --hash=sha256:8cd9f7203c0b2aaed66809edf7e66aa3ab0fe3402e87b69a43b9dfd8d33125ab \ + --hash=sha256:8d56340493496d50ccc41b460610c1ce6a197aac710733b5f36910e8c9f3ba6d \ + --hash=sha256:90f766c5f8e55de2fe65e4241e3e2e46ed7528e7931255a7ed0dfcb5ce622b15 \ + --hash=sha256:921408c159b01d39d70e90252b8ab17f16594fc91f229e6f881642fb0ed24ae7 \ + --hash=sha256:928d83b72808dc73a5df530b7fc27101052be1baf013a5dd75a1535de6cf107e \ + --hash=sha256:970f9ff27d12e089fa342379f52ea3f4aff6fbe8690aca9a1645c14aee5d08fb \ + --hash=sha256:97caee7e4c3e20dff9e6adca0b7443c3cf9d7546ed5d0750954c5bb5456bad86 \ + --hash=sha256:987e191700873419cc23d94d4212e57a85df24eebbe9a33785907b0c99a5a57a \ + --hash=sha256:9b59ead8dd9a96399cc38994d19720443a3cc626b730cbb4f414fb768b3e2816 \ + --hash=sha256:9d522e95bffac7338178757a7931b81639b9e0f2a3ee6e8c7ffdf867f2bfed36 \ + --hash=sha256:9ef1920b423effe2837351d19a2278d7a516404a07200cca30b881077a2d7877 \ + --hash=sha256:a054959ec07f2fd63b6e8a63019a6879262c4f1983a100545c5a0206eefe993e \ + --hash=sha256:a2e699d5f290f81829f42638f8bc6582e3e73452d8607edf749ad3e1843946fa \ + --hash=sha256:a38a21efd05384fb82d35bed81fac0ff6056ea39c3dee3c293885ce910879dd0 \ + --hash=sha256:a41209acca3ade45d27ed665a20f8d174d5bb10c3bf0881802f5215e3269fadb \ + --hash=sha256:aa03ac78c7806c6a391c037e0a63552e11532210b719bc062cddc00671a7577f \ + --hash=sha256:ab7b316bba31be494635dcc5db87e429f2478073d15d2c54925c32fd9e1947f4 \ + --hash=sha256:ad11c9153c775087d261634410da7cfaac2743d79bc9ab573177d9e3398f00c6 \ + --hash=sha256:ad8bdad17cfc64aefb049e53687ff8730a72e2c3d99edcb36001683122597846 \ + --hash=sha256:add6b3827cbd6ce068ad70b1b890d44271801386a726e2bafe5bced784466642 \ + --hash=sha256:aea27aa0927b0423a0cfb167bd505c2dc59d1df65c66372204e43ba94fc964a8 \ + --hash=sha256:af85ae40c71d422fad944aa8666d59374e4fa92f77899fce34b984037db41420 \ + --hash=sha256:b2ab962524adb39dbad565fd259e15a1c26b8944fa978c24ed6dea5ab1eeefd0 \ + --hash=sha256:b305657e2ddc29a50b333053e7c7f431a8c24c92b7dcbbf7a420f2330152b486 \ + --hash=sha256:b3967550c8952bc516c79c40726a54313aceeb3162a8d5cc655362ab83d0957c \ + --hash=sha256:b8bd6743ad58fe6067ea1677d5df4674bd7de143b038bcd4129c3a6ced483ae8 \ + --hash=sha256:b8d019e935e4f8d6493690036161e62fae033891b71f20d238342ae266fec852 \ + --hash=sha256:bbe0374e18beadac588f47e10cd14cf8b06395dc982062b643c5e3690355bfe3 \ + --hash=sha256:bc6df52a60b521c7b7d69de0c14856397d3cce1e39aa22cfe439c350d6f52524 \ + --hash=sha256:bde35c0d6b5a204990f43e4ab43b6e3e4d5a1de773246e11d518945e3ba789ed \ + --hash=sha256:c2c670cd7aaad2a3bff450addb32b26aa831f82a8b6c2c875ec19bb282a6c45d \ + --hash=sha256:c3e26771a0759d213e60c885012e1f75ad84897f3d6b56b65092fbc93615bc24 \ + --hash=sha256:c51915961a51e37403fd94114e293d580dd916ddd1961b229217a87193d2454e \ + --hash=sha256:c5d13a4ccf3fc9a00fb4e8cae818ad7ecf33f210d8098fecbfc087ff43573544 \ + --hash=sha256:c626f68524a19f50d9a9babc17f9c379d1b2a9f2a3da5ac3c40a205cc736259f \ + --hash=sha256:cca83e86a300db6c72847bc7acc259bf86481063aea408b07c8a96d649797b7f \ + --hash=sha256:cd835565b660ca125f5895105981d691c708c15367b88a69fa4d92ddbe24504a \ + --hash=sha256:cea0a63173e4ae98cd960f484096233da76a62550ac10c53312a69ad9f3545b1 \ + --hash=sha256:d2e29a0dd1d33e49623d4c69bfa7e6d3d5c7530cf42bebe612cff965acffd1a9 \ + --hash=sha256:d4a731cc7cd513bf4c4016a24a060fb1aa8475e8682e1f8b1bfb836f8d3f50f0 \ + --hash=sha256:d7945560fc6ce687ea83aa0bc375aa8a1101d9eee1fcbd085c5e0a5b6c6ac8ad \ + --hash=sha256:dae3765f731779faa947715485f6794bc5984802be4584478a3e9e5143dd62e1 \ + --hash=sha256:dc8510c8b5b8373e0789ca05ebffc0aaab6e8a8f86d67956c91bc37f43d4f989 \ + --hash=sha256:dd55ca435d6c3c7e4cb6d8a0a98a133d4fd1b67d9abf90449442d9f5a728a9ff \ + --hash=sha256:dfceda99f3105e9e6fce8dfd157f80894ad20247dc9ffce368c8b7883e7a2aac \ + --hash=sha256:e0652b2110fc374c766cdfca4fad61f9d13a0ad60c5b335ef3fed509374557bc \ + --hash=sha256:e1fa46cb8ddbfba2adf8277b8225e2ebf5bae435e2251c730c17bc0020f63c5e \ + --hash=sha256:e6926204905e1a2f278bacf92ff2fe31343bcc7fb9ff08fdd42be66b3a217ef0 \ + --hash=sha256:e9359bfd0efd12593f0db40ccb2d1497284401da207f1d6a1783718313201b21 \ + --hash=sha256:e9f1625d047d011804a3dde0b8c5099ca2230224ca6b17f13a97b5531799c3aa \ + --hash=sha256:ec570979304a529a8be1bf9ea28889742a2ff5de9af1c6734584dfe1645da3e6 \ + --hash=sha256:ee87d8c4a4ebbef1c7cb2cf251a1d77726ef06a1597ed04d3dce92709b8fe0f1 \ + --hash=sha256:f9d26982045b28db1937ac60682a9940fdb72f9cab3421a5d56c03f2207c99e9 \ + --hash=sha256:fb37ec7d7542e2f23fd7ca8fd034c8db7221c5e86d6a6a3a170711f993eecf15 \ + --hash=sha256:fbae9b1a4d70e2283d71a0b66db2a91eb1a2cefaf370e47eff3a79f8ece7148d \ + --hash=sha256:fc115cca04dbdfd98a67ec89ba5ffd8a87f3201171af54980cfd550997611c41 \ + --hash=sha256:fd26d4b182b7138fc948cda55fe2e91b70d987731e169e628f42ba22cc6e3cce \ + --hash=sha256:ff3b33d8c8dbbe32936d2056296324371a07ed0b29177e2eb8ec46569436817f + # via py-boost +wrapt==2.4.0 \ + --hash=sha256:0191d717dfbb8e519e7bfd4775e5b9bd57e359b3a09ab5db1ea47f6025b4d845 \ + --hash=sha256:0536f5d85ff6a157ebe7e0fe08c5479943742cf1ce59569075a66159efcbc495 \ + --hash=sha256:07daab5babb7edaf89413f5c8bd638474540fb2643b5dfb685bdc0680c96803a \ + --hash=sha256:08d8378c4514ac8dcc0ace76044cf87a873e6a52b5e6109834c8fb9037f4441b \ + --hash=sha256:09064c7be688c38c3ff125ce86bc26b69b5d78dd56062c3ddd9c814b2a25f1e1 \ + --hash=sha256:0972cd025f4c86fa2d8abd953d9f875779935343af58b4ce019ff89573fc65bd \ + --hash=sha256:0b8851a54b137eec9a8480d73cc1a309613e6a465d6f157300f1eb6b5b7c0505 \ + --hash=sha256:0eca69c9e93518240abe8801fb9b2726116a6e48172e4564c2651a2e14521747 \ + --hash=sha256:11ccb5f3de2047ef91408464abdc04682e40e7d7bc9614885d2abcaa7e2ef149 \ + --hash=sha256:15bb88c0a6c6312244917bb0a094368746fefb92663209363e16f20972a57b34 \ + --hash=sha256:1656de3835f760781c9b974bce07d8c04edb9c9ad7ad67264aee69cd68a1db09 \ + --hash=sha256:174f3576dacf55c8a7c21719d4b7c8088efb991888db5728cfc891b80b28853f \ + --hash=sha256:18aabd9301d06026f5900538051773d6f87f65ae02cdc60de482df978513dc0a \ + --hash=sha256:28f5de1526831b8f173889a436e289fe181ede8c66c9feb669d1aca8fd602eaf \ + --hash=sha256:2a9f1a2f75bb95257cc5744e255e10a5a86e923f328b40ad3dbf9d8d03430013 \ + --hash=sha256:2dc0f6412aaf5fc7e6a3abf119b7c671dbd026303daccac20112c046a48b68b9 \ + --hash=sha256:325c24cfddb46f93c931cf37fa3a9929ac94e70a5627efccd51283f9fd69c6db \ + --hash=sha256:328eb2d978ca3a6ae25f8d8fe560bf8f4bc9778b5932e7b142664eef05b92e8f \ + --hash=sha256:332d9bad7e9b718974bb2a576504c4956f45b4a0fcd7b3bb7827279167550464 \ + --hash=sha256:3367a5212212c9393e0d3ca6ae029b3a8fa40c5896e4a985d43fe8a4b8322f0d \ + --hash=sha256:36b56a4fba13b34ed8ff307557325fff215de0a58b5dbaef2c50e4d8aa39dbd1 \ + --hash=sha256:37ba372e9ae71ec43e165b5db05f52e71f7c07dafb9d6a254ef7128112dce751 \ + --hash=sha256:39cd68df4dff79f5336f9c745c06259d204bcb42d504040c9c91eac9e2abb39c \ + --hash=sha256:3a69161cae7f0dca44c89c1d14146b4a0508a0c3cad98b3f2db1f4e9016c94ba \ + --hash=sha256:3d5e5eb76fb87e62752af751d2dcd9d1cd986b12037d2e1363d109ba716029e8 \ + --hash=sha256:413e757dce7a43fcda8bb8441994b1127492ffac6a5803af777d44516df8c6e2 \ + --hash=sha256:430fde1a116df3ceb5c29035de1da6609b70e680d9b8ce3ee624422f3fe0978c \ + --hash=sha256:4597d19904b4aa97331d8bb651ac626d9397727e717942cf11bd7699ff97aa45 \ + --hash=sha256:49bb5a572469e0e18163a8ec2aa972135a0929899ecbe627665f274506e1b5b4 \ + --hash=sha256:4a1d591386ec4aa99780f672232868620f4f3b63401e2ceb529762580ef8c54e \ + --hash=sha256:4f8ddff4bbb75916be36da5169b8b9d475b59a1bd24acdb7551bb2c71be9aaac \ + --hash=sha256:52f01626f1d2bc54585954cd8b4931f81003b0ac8dad61c741f43014bc9a0f0b \ + --hash=sha256:5b8fff692f74782de89ba9d7b526a7cc398569b6a988ddc848159cc033c86237 \ + --hash=sha256:5c5c4c728cd22a36e4b8bb5df4a7d3bccaa865d27725b36eeb3b6f18fb2e1bc2 \ + --hash=sha256:5cc2e7c7b6032e11a2b367a9baadaf0c5241feff2d8205260d87f1aa6dbdf84b \ + --hash=sha256:5f041ed6a4d571010944bd6cfad9072db463e1851877b6d3227467a44af37456 \ + --hash=sha256:5f3bdfc35c83b562fcaebc0f24593045e5ed9f3b633adafd35222718a0ec38fa \ + --hash=sha256:635cc171ddfd72edff10e295a02daa65edaa1c0ba619ad11eeed15cd2258c5df \ + --hash=sha256:637fd6a18bb668a0c27b4767dcbc2fa93119c90da735bd2669fdde2d7b59fab3 \ + --hash=sha256:63b94f401d7ae3a9a3027472fd3a3ff38afd2ed293b2f0b3b84a6d133a9f99a3 \ + --hash=sha256:6436e2bda993a3eb69a1b317fc831c8ebcafb5704c390859ebd49f81218c4bbb \ + --hash=sha256:643e45aa88698c8aae938c50e61940985d4ab9e53ea666d3e8e4eb86a4820d0f \ + --hash=sha256:648d1d4f94e8a0a1656675c755f40d2f0ee5fe92c449ab45326f4ecc2738cbe8 \ + --hash=sha256:66e7512c0d324cc37bba1def2be1fc365cbb685d3aa393a8f6f4d2d00202881d \ + --hash=sha256:694005fdc3002ade0f21641408c588028abde03c85961f3ba7727d8bead3ed6b \ + --hash=sha256:6a48bc764deb585d3b8862544ca12df417109984e018767b4ac9aa46bbb55ccd \ + --hash=sha256:6b3e082d43f592fcd381aee46354a11ce887a813ce5bbcedd9766fd681723c09 \ + --hash=sha256:6b9b32d5e4f0a179cef5075cc79b79d6d3482c44c434c12969e48c6719e06d95 \ + --hash=sha256:6d57264c9dfcf37d2bf0b0fbec68d0f6184fc5617267619ada04d03e8b0231f3 \ + --hash=sha256:7082fc1f94b020ac275870c4af71b09cff22876fe6e9c4c0ad01ea21d217b288 \ + --hash=sha256:717dd7ef439863933664951f89902b7e0ee3652293543cb9917c2e16bbde1949 \ + --hash=sha256:72826910a1cf5a081234720fd43011304b899acfee219af49148155b4d795533 \ + --hash=sha256:75529a2fb569a671cf162f762c1b576f569f571b55ec7f3481258ca842ba507f \ + --hash=sha256:75944792cf6b99262d649d55710bf5901f7013fbb212c7a1d736b97a20517607 \ + --hash=sha256:788e473d1a6786d29d577b1e2bd95e214c09cdafde84907c522c31069c9acfac \ + --hash=sha256:7a057d376d994da6bd1bbf955ecfda699aa7353826f98847f5605e1801abdfd4 \ + --hash=sha256:7a27d9653e0f88aa06598954337a545fc3f75bc811df897157a8614846d18d9c \ + --hash=sha256:7d28f8f35a02d49f75f57fa4e755db4ba33f65841c0de64cd65b253916f5bf06 \ + --hash=sha256:7de5b8d94417e55c02be50cc226e0ae1209bbc73813bf691dff3979c94438115 \ + --hash=sha256:811a36628d8b76724b980d508d576e5c5ecae1073b6ec4b4eb21646921906fe6 \ + --hash=sha256:85ed3c67fd39e8d9a36c224758cb6f2f4eb277d07ea677930caa0008c18ec002 \ + --hash=sha256:8763ad01e3725b7751a4575f38bbcc19c0aa0822fec91c5c5bd21ce3ce7e1d2b \ + --hash=sha256:8828369b7d3e93c547cc8ad931b5a57b4e8d174035c82762fb1091e7d05ac9f5 \ + --hash=sha256:8b464316489fb2fca0669ea0f8f07290054a0f26fc72982d3e4cf95469628ba9 \ + --hash=sha256:9125c6dbe8b88c00dd8ef4fc1e55757e8eb4720b6b2b2cc610a45bd32bd28c57 \ + --hash=sha256:93180c2199784dd6a1075b33f9ed636bd0966821edbece6b3d5379b1c4f0bb7d \ + --hash=sha256:932a8892265df7b71257c30e5752635bc1f06a8c4e264024ff031bdf9bb10918 \ + --hash=sha256:947bd4b3438167b3638bf5477cb83a068a586ffb6d331ac427f39839c2b93b3c \ + --hash=sha256:9905bceb7b2833559518574ad6259d2ec9ffd111a0aa330ca685db74478e1ae3 \ + --hash=sha256:9c884240e7415d3a384e70a15ceea0e884cc9289bcc254afd6412d4e7cf99c47 \ + --hash=sha256:a1117c63a39ba4d1b884e658089e512412d5174217ea1b4fe570977e42a5b129 \ + --hash=sha256:a112a1bfdd2621e4344cb0a32dbaab80636b32dac1b055d03fbb2a67d806d1db \ + --hash=sha256:a67ec80d15ac199d4a9a04a33f3039a1c219c9bf1c07b1b0422497613f167fb9 \ + --hash=sha256:a9ca1cdb3f7facb4990c7739ea5afbaceeb6728d066feedde03a4cfe83b29b03 \ + --hash=sha256:abc347e92f9202c8ac1d5c1626a800fd5e56e13433f0651b26dddda5b421ac79 \ + --hash=sha256:b1737f46b1e4a81eb93500a7f2854319e1c7a86e8863fb050b7b4daadd5a4178 \ + --hash=sha256:b33df90f3d1e5b1c8811830b11a3e718b4f3a2823b748fa9be1688cb82b193f1 \ + --hash=sha256:be535bdfbedda84cb8ebc6a80955dfd03d46840c13470486bd038f089e38b172 \ + --hash=sha256:c246aaed719dcdb62eeb7b8d9306a6237777226ef3baad35919c4ae134c91ce7 \ + --hash=sha256:c4b42c92df24a986da7e2b5b44fb142504d858c54a276f9f366983ca4482dfda \ + --hash=sha256:c4fca1e63af6675af3df7cdfcd5a0c878b5e655c7e48611ced9dc8d62183a11d \ + --hash=sha256:d294576fddac636589e4deccfe782e8f429da10f167c1985c4d51071de3672b7 \ + --hash=sha256:d5f45bead708e2c0014be5e98531ce7202916b098a208c7be83c6ceb0a2559fa \ + --hash=sha256:d7c496a966d70f8caf215faddc00de9931a5bf652664221b785a73b20229a696 \ + --hash=sha256:d7dbbdbfdacb85c2d962fa52db791c77943fd777d600d74c95af2d53b32f5a94 \ + --hash=sha256:d8e6e1e5dc684dfce7c33fc8b67a08ba2af94f3a45cfc70d5c1d6a839d2caf97 \ + --hash=sha256:db1285071ea09a7767fac608e7b5c7b03c09833b06186875a359905fbc659d29 \ + --hash=sha256:e084558fbd112d2e1e34b0f5c71e45a3405bdad51a17150368a959bcf6697964 \ + --hash=sha256:e52c6a5be3284719e53b629ccfa565c146e604e861de35e861c94f7622806eb5 \ + --hash=sha256:e78c947e18fadfd690c9420c30a96d221feeb93fc8f1cc00509b370ac16c3114 \ + --hash=sha256:e8df31a126a0a247c1aa379e30873839de03912dea09ca360c680f3625d815df \ + --hash=sha256:e9e7e94472f0e3f1447caf27e1939eb384d0e87972a35a05f5c2e0968e9c01af \ + --hash=sha256:e9f8017443595870aa31f46125553a5c55ce95a26a267b96261baee6ba566d83 \ + --hash=sha256:ef4e2d6e399ce6eecc80179a6b9ef6544f121288f95fc132bc36c9d9503903af \ + --hash=sha256:ef9797bf7c6f9ad9d294538c4f9a64ef3dbbadb63590a9a067393fd49ba28b0f \ + --hash=sha256:efd9a4be6785295e471f71efdf5682bd11d5b822b9665e6e1b4844917cf2f7ac \ + --hash=sha256:f1e9e088094f4895f84ab043e7d59401df137d663efbf1e80c82144882960830 \ + --hash=sha256:f43af38a642c3d6062e9740d8f5cc0feb5dbe0da516702df892147393b8cb14d \ + --hash=sha256:f53837b56ca834f381d300621f8c9525b9da517331ce0f4b805ed08f63bedcd7 \ + --hash=sha256:f7fed45dbadf5d98a52bfff9624d3cca00affeb9543d493c9632b7a53cdd35c9 \ + --hash=sha256:fc1b2cebd6d8db9b4ac0adc817c08b4901922e85604ae2a69aecb5217b2c09d8 + # via formulaic +xgboost==3.4.1 \ + --hash=sha256:1ea15f15f661825b6a67d87674fb9604a1abb38dd0d4c5cf0486fc85f5203e83 \ + --hash=sha256:2d30fa513673101f542fdcbd18f30c8f96c064046f798635ac08663e9969f81b \ + --hash=sha256:6968a4c71efdfa859df0dfcad0d99211c95c28c4ffd6aecff46efff77d18026a \ + --hash=sha256:6adf2afa396da2ae8ed30295b50b99d4712eed9a6e0ce6cfe069290e4335e51f \ + --hash=sha256:7faaf99de26719c22bfae883a02bd56b5a3c2203122616e563cc72b7191b5c96 \ + --hash=sha256:a7afd7dbace0951c93aa85ffe046e54bc40893f5b51cd3e7991eb157bf9c7c7c \ + --hash=sha256:e9312b30e5679d27c1d8b9ee97e092b964d960a672d5d406d9fb3cd0845c9797 + # via -r benchmarks/v1/requirements-cpu.in +xlrd==2.0.2 \ + --hash=sha256:08b5e25de58f21ce71dc7db3b3b8106c1fa776f3024c54e45b45b374e89234c9 \ + --hash=sha256:ea762c3d29f4cca48d82df517b6d89fbce4db3107f9d78713e48cd321d5c9aa9 + # via -r benchmarks/v1/requirements-cpu.in diff --git a/benchmarks/v1/search-design.json b/benchmarks/v1/search-design.json new file mode 100644 index 0000000..b288a99 --- /dev/null +++ b/benchmarks/v1/search-design.json @@ -0,0 +1,716 @@ +{ + "budgets": { + "agent_attempt_wall_s": 1800, + "agent_attempts_per_task_arm": 3, + "agent_generated_tokens": 20000, + "cpu_threads": 2, + "gpu": "T4", + "gpu_count": 1, + "gpu_memory_mib": 15360, + "gpu_trial_wall_s": 1800, + "ram_mib": 8192, + "trial_retries": 0, + "trial_wall_s": 1800 + }, + "families": { + "catboost": [ + { + "depth": 4, + "l2_leaf_reg": 0.0, + "learning_rate": 0.03, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 0.0, + "learning_rate": 0.03, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 10.0, + "learning_rate": 0.03, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 10.0, + "learning_rate": 0.03, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 0.0, + "learning_rate": 0.03, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 0.0, + "learning_rate": 0.03, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 10.0, + "learning_rate": 0.03, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 10.0, + "learning_rate": 0.03, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 0.0, + "learning_rate": 0.1, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 0.0, + "learning_rate": 0.1, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 10.0, + "learning_rate": 0.1, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 4, + "l2_leaf_reg": 10.0, + "learning_rate": 0.1, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 0.0, + "learning_rate": 0.1, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 0.0, + "learning_rate": 0.1, + "rounds": 1000, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 10.0, + "learning_rate": 0.1, + "rounds": 300, + "seed_from_fold": true + }, + { + "depth": 6, + "l2_leaf_reg": 10.0, + "learning_rate": 0.1, + "rounds": 1000, + "seed_from_fold": true + } + ], + "formula_global": [ + { + "initial_amplitude_multiplier": 0.5, + "initial_rate": 0.1, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 0.5, + "initial_rate": 0.5, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 0.5, + "initial_rate": 1.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 0.5, + "initial_rate": 2.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 1.0, + "initial_rate": 0.1, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 1.0, + "initial_rate": 0.5, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 1.0, + "initial_rate": 1.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 1.0, + "initial_rate": 2.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 2.0, + "initial_rate": 0.1, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 2.0, + "initial_rate": 0.5, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 2.0, + "initial_rate": 1.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 2.0, + "initial_rate": 2.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 4.0, + "initial_rate": 0.1, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 4.0, + "initial_rate": 0.5, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 4.0, + "initial_rate": 1.0, + "max_nfev": 2000 + }, + { + "initial_amplitude_multiplier": 4.0, + "initial_rate": 2.0, + "max_nfev": 2000 + } + ], + "glm": [ + { + "alpha": 1e-06, + "max_iter": 1000 + }, + { + "alpha": 3.414548873833601e-06, + "max_iter": 1000 + }, + { + "alpha": 1.165914401179831e-05, + "max_iter": 1000 + }, + { + "alpha": 3.9810717055349695e-05, + "max_iter": 1000 + }, + { + "alpha": 0.00013593563908785255, + "max_iter": 1000 + }, + { + "alpha": 0.00046415888336127773, + "max_iter": 1000 + }, + { + "alpha": 0.001584893192461114, + "max_iter": 1000 + }, + { + "alpha": 0.0054116952654646375, + "max_iter": 1000 + }, + { + "alpha": 0.018478497974222907, + "max_iter": 1000 + }, + { + "alpha": 0.0630957344480193, + "max_iter": 1000 + }, + { + "alpha": 0.21544346900318823, + "max_iter": 1000 + }, + { + "alpha": 0.7356422544596406, + "max_iter": 1000 + }, + { + "alpha": 2.5118864315095824, + "max_iter": 1000 + }, + { + "alpha": 8.576958985908945, + "max_iter": 1000 + }, + { + "alpha": 29.286445646252375, + "max_iter": 1000 + }, + { + "alpha": 100.0, + "max_iter": 1000 + } + ], + "lightgbm": [ + { + "lambda_l2": 0.0, + "learning_rate": 0.03, + "num_leaves": 15, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.03, + "num_leaves": 15, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.03, + "num_leaves": 15, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.03, + "num_leaves": 15, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.03, + "num_leaves": 31, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.03, + "num_leaves": 31, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.03, + "num_leaves": 31, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.03, + "num_leaves": 31, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.1, + "num_leaves": 15, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.1, + "num_leaves": 15, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.1, + "num_leaves": 15, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.1, + "num_leaves": 15, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.1, + "num_leaves": 31, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 0.0, + "learning_rate": 0.1, + "num_leaves": 31, + "rounds": 1000, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.1, + "num_leaves": 31, + "rounds": 300, + "seed_from_fold": true + }, + { + "lambda_l2": 10.0, + "learning_rate": 0.1, + "num_leaves": 31, + "rounds": 1000, + "seed_from_fold": true + } + ], + "ngboost": [ + { + "learning_rate": 0.03, + "minibatch_frac": 0.7, + "rounds": 300, + "weak_depth": 2 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 0.7, + "rounds": 1000, + "weak_depth": 2 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 1.0, + "rounds": 300, + "weak_depth": 2 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 1.0, + "rounds": 1000, + "weak_depth": 2 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 0.7, + "rounds": 300, + "weak_depth": 4 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 0.7, + "rounds": 1000, + "weak_depth": 4 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 1.0, + "rounds": 300, + "weak_depth": 4 + }, + { + "learning_rate": 0.03, + "minibatch_frac": 1.0, + "rounds": 1000, + "weak_depth": 4 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 0.7, + "rounds": 300, + "weak_depth": 2 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 0.7, + "rounds": 1000, + "weak_depth": 2 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 1.0, + "rounds": 300, + "weak_depth": 2 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 1.0, + "rounds": 1000, + "weak_depth": 2 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 0.7, + "rounds": 300, + "weak_depth": 4 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 0.7, + "rounds": 1000, + "weak_depth": 4 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 1.0, + "rounds": 300, + "weak_depth": 4 + }, + { + "learning_rate": 0.1, + "minibatch_frac": 1.0, + "rounds": 1000, + "weak_depth": 4 + } + ], + "paid_composition": [ + { + "count_alpha": 1e-06, + "max_iter": 1000, + "severity_alpha": 1e-06 + }, + { + "count_alpha": 1e-06, + "max_iter": 1000, + "severity_alpha": 0.001 + }, + { + "count_alpha": 1e-06, + "max_iter": 1000, + "severity_alpha": 1.0 + }, + { + "count_alpha": 1e-06, + "max_iter": 1000, + "severity_alpha": 100.0 + }, + { + "count_alpha": 0.001, + "max_iter": 1000, + "severity_alpha": 1e-06 + }, + { + "count_alpha": 0.001, + "max_iter": 1000, + "severity_alpha": 0.001 + }, + { + "count_alpha": 0.001, + "max_iter": 1000, + "severity_alpha": 1.0 + }, + { + "count_alpha": 0.001, + "max_iter": 1000, + "severity_alpha": 100.0 + }, + { + "count_alpha": 1.0, + "max_iter": 1000, + "severity_alpha": 1e-06 + }, + { + "count_alpha": 1.0, + "max_iter": 1000, + "severity_alpha": 0.001 + }, + { + "count_alpha": 1.0, + "max_iter": 1000, + "severity_alpha": 1.0 + }, + { + "count_alpha": 1.0, + "max_iter": 1000, + "severity_alpha": 100.0 + }, + { + "count_alpha": 100.0, + "max_iter": 1000, + "severity_alpha": 1e-06 + }, + { + "count_alpha": 100.0, + "max_iter": 1000, + "severity_alpha": 0.001 + }, + { + "count_alpha": 100.0, + "max_iter": 1000, + "severity_alpha": 1.0 + }, + { + "count_alpha": 100.0, + "max_iter": 1000, + "severity_alpha": 100.0 + } + ], + "xgboost": [ + { + "learning_rate": 0.03, + "max_depth": 4, + "reg_lambda": 0.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 4, + "reg_lambda": 0.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 4, + "reg_lambda": 10.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 4, + "reg_lambda": 10.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 6, + "reg_lambda": 0.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 6, + "reg_lambda": 0.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 6, + "reg_lambda": 10.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.03, + "max_depth": 6, + "reg_lambda": 10.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 4, + "reg_lambda": 0.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 4, + "reg_lambda": 0.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 4, + "reg_lambda": 10.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 4, + "reg_lambda": 10.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 6, + "reg_lambda": 0.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 6, + "reg_lambda": 0.0, + "rounds": 1000, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 6, + "reg_lambda": 10.0, + "rounds": 300, + "seed_from_fold": true + }, + { + "learning_rate": 0.1, + "max_depth": 6, + "reg_lambda": 10.0, + "rounds": 1000, + "seed_from_fold": true + } + ] + }, + "limits": [ + "Configurations are not claims of optimal tuning.", + "Method adapters must translate round counts and early stopping without ignoring parameters.", + "GLM and global formula searches use convergence budgets, not tree-only parameters.", + "Train-many M=1/8/32 sets are separate from these 16-trial E3 searches.", + "A6 target scaling and all categorical vocabularies fit training only.", + "Resource caps require actual host/container enforcement; the process runner alone enforces wall time and thread environment only." + ], + "schema": "openboost-search-design-v1", + "selection": { + "failed_trial": "record failure; no replacement or free retry; required search cannot pass", + "folds": 5, + "primary_only": true, + "test_access": "after all validation trials and method selection", + "trials_per_method": 16 + }, + "shared": { + "aft_sigma": 1.0, + "bin_budget": 255, + "early_stopping_rounds": 50, + "probability_epsilon": 1e-15, + "quantiles": [ + 0.1, + 0.5, + 0.9 + ], + "tweedie_evaluation_power": 1.5 + }, + "status": "configuration design fixed before quality runs; dataset/matrix and agent-cohort completion pending" +} diff --git a/benchmarks/v1/secondary_smoke.py b/benchmarks/v1/secondary_smoke.py new file mode 100644 index 0000000..d3c0485 --- /dev/null +++ b/benchmarks/v1/secondary_smoke.py @@ -0,0 +1,129 @@ +"""Weighted probabilistic and parametric comparator smoke on installed CPU packages.""" + +import importlib.metadata +import json +import pickle +import platform +from pathlib import Path + +import numpy as np + + +def run(): + from ngboost import NGBRegressor + from ngboost.distns import Normal + from ngboost.scores import LogScore + from scipy.optimize import least_squares, minimize + from scipy.special import log_ndtr + from sklearn.linear_model import GammaRegressor, PoissonRegressor, TweedieRegressor + from sklearn.tree import DecisionTreeRegressor + + rng = np.random.default_rng(73) + x = rng.normal(size=(80, 3)) + y = x[:, 0] + rng.normal(size=80) * 0.3 + w = np.linspace(0.5, 2, 80) + records = [] + model = NGBRegressor( + Dist=Normal, + Score=LogScore, + Base=DecisionTreeRegressor(max_depth=2, random_state=73), + n_estimators=4, + random_state=73, + verbose=False, + ) + model.fit(x, y, sample_weight=w) + dist = model.pred_dist(x) + nll = np.log(dist.scale) + 0.5 * ((y - dist.loc) / dist.scale) ** 2 + 0.5 * np.log(2 * np.pi) + np.testing.assert_allclose(nll, -dist.logpdf(y), rtol=1e-10, atol=1e-10) + loaded = pickle.loads(pickle.dumps(model)) + np.testing.assert_array_equal(loaded.pred_dist(x).loc, dist.loc) + records.append( + { + "case": "NGBoost weighted Normal", + "status": "pass", + "mean_nll": float(np.average(nll, weights=w)), + } + ) + exposure = np.linspace(0.1, 2, 80) + count = rng.poisson(exposure * np.exp(x[:, 0] / 3)) + positive = np.exp(y / 3) + for name, klass, target, weight, kwargs in [ + ("Poisson rate/exposure", PoissonRegressor, count / exposure, w * exposure, {}), + ("Gamma severity", GammaRegressor, positive, w, {}), + ( + "Tweedie annualized", + TweedieRegressor, + count / exposure, + w * exposure, + {"power": 1.5, "link": "log"}, + ), + ]: + m = klass(alpha=0.1, max_iter=1000, **kwargs).fit(x, target, sample_weight=weight) + before = m.predict(x) + after = pickle.loads(pickle.dumps(m)).predict(x) + assert np.isfinite(before).all() and np.all(before > 0) + np.testing.assert_array_equal(before, after) + records.append( + { + "case": name, + "status": "pass", + "reload_max_abs_error": float(np.max(np.abs(before - after))), + } + ) + # Fixed-sigma log-normal linear AFT, independent of tree implementations. + design = np.column_stack([np.ones(80), x]) + observed = np.exp(y) + event = np.arange(80) % 4 != 0 + + def loss(beta): + z = np.log(observed) - design @ beta + terms = np.where( + event, np.log(observed) + z * z / 2 + 0.5 * np.log(2 * np.pi), -log_ndtr(-z) + ) + return float(np.average(terms, weights=w)) + + fit = minimize(loss, np.zeros(4), method="BFGS") + if not fit.success or not np.isfinite(fit.fun): + raise ValueError("AFT optimizer failed: " + fit.message) + records.append( + {"case": "fixed-sigma linear lognormal AFT", "status": "pass", "mean_nll": float(fit.fun)} + ) + age = np.linspace(0.1, 4, 80) + truth = 3 * (-np.expm1(-0.7 * age)) + + def residual(raw): + a, b = np.logaddexp(0, raw) + return np.sqrt(w) * (a * (-np.expm1(-b * age)) - truth) + + fit = least_squares(residual, [1.0, 0.0], max_nfev=2000) + np.testing.assert_allclose(np.logaddexp(0, fit.x), [3, 0.7], rtol=1e-6, atol=1e-7) + records.append( + { + "case": "global saturating formula", + "status": "pass", + "max_residual": float(np.max(np.abs(fit.fun))), + } + ) + return { + "scope": "tiny synthetic comparator smoke, not real quality", + "records": records, + "environment": { + "python": platform.python_version(), + "os": platform.platform(), + "packages": {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + }, + } + + +if __name__ == "__main__": + import hashlib + import subprocess + + result = run() + result["source_sha"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + result["source_file_sha256"] = hashlib.sha256(Path(__file__).read_bytes()).hexdigest() + result["dirty"] = bool(subprocess.check_output(["git", "status", "--porcelain"])) + Path("benchmarks/v1/evidence/secondary-cpu.json").write_text( + json.dumps(result, indent=2, sort_keys=True) + "\n" + ) + print(result["records"]) diff --git a/benchmarks/v1/selection.py b/benchmarks/v1/selection.py new file mode 100644 index 0000000..4979cdd --- /dev/null +++ b/benchmarks/v1/selection.py @@ -0,0 +1,254 @@ +"""Independent validation selection and sealed test-feature release. + +Protocol and receipt digests must be held by the trusted orchestrator, separately +from producer artifacts. This API orders access; it is not an OS sandbox. +""" + +import hashlib +import json +import re +from pathlib import Path + +import numpy as np + +from benchmarks.v1.judge import read_json +from benchmarks.v1.preprocessing import fit_target_scale +from benchmarks.v1.quality import metrics +from benchmarks.v1.quality_report import PRIMARY, load + + +def digest(value): + return hashlib.sha256( + json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode() + ).hexdigest() + + +def _fields(value, fields): + if not isinstance(value, dict) or set(value) != set(fields.split()): + raise ValueError("invalid object fields") + + +def _hash(value): + if not isinstance(value, str) or not re.fullmatch("[0-9a-f]{64}", value): + raise ValueError("invalid hash") + + +def _bytes(root, entry): + _fields(entry, "path sha256") + _hash(entry["sha256"]) + rel = Path(entry["path"]) + if rel.is_absolute() or ".." in rel.parts or not rel.parts: + raise ValueError("unsafe artifact path") + path = (root / rel).resolve() + if not path.is_relative_to(root): + raise ValueError("artifact escapes root") + raw = path.read_bytes() + if not raw or hashlib.sha256(raw).hexdigest() != entry["sha256"]: + raise ValueError("artifact hash mismatch or empty artifact") + return raw + + +def _ids(arrays): + ids = arrays["row_ids"] + if ids.ndim != 1 or not len(ids) or len(np.unique(ids)) != len(ids): + raise ValueError("invalid row IDs") + if ids.dtype.kind not in "iuUS": + raise ValueError("integer or string row IDs required") + return ids + + +def _disjoint(a, b): + if a.dtype.kind != b.dtype.kind: + raise ValueError("inconsistent row ID types") + if np.intersect1d(a, b).size: + raise ValueError("partition row overlap") + + +def audit(protocol, records, directory, pinned_protocol_sha256): + """Recompute all validation scores; never open the test-feature artifact.""" + _hash(pinned_protocol_sha256) + if digest(protocol) != pinned_protocol_sha256: + raise ValueError("changed protocol") + _fields( + protocol, + "schema application fold identity train_rows validation test_features selection_weights methods" + + (" train_targets target_scale" if protocol.get("application") == "A6" else ""), + ) + if protocol["schema"] != "openboost-selection-v1": + raise ValueError("unknown selection protocol") + app = protocol["application"] + if ( + app not in {*PRIMARY, "A6"} + or type(protocol["fold"]) is not int + or protocol["fold"] not in range(5) + ): + raise ValueError("invalid application/fold") + _fields(protocol["identity"], "code data split preprocessing environment search_design") + for h in protocol["identity"].values(): + _hash(h) + # Validate its descriptor without reading its contents before selection. + _fields(protocol["test_features"], "path sha256") + _hash(protocol["test_features"]["sha256"]) + root = Path(directory).resolve() + training = load(root, protocol["train_rows"]) + _fields(training, "row_ids") + train_ids = _ids(training) + truth = load(root, protocol["validation"]) + if not {"row_ids", "y"} <= set(truth) or set(truth) - { + "row_ids", + "y", + "weight", + "event", + "query", + }: + raise ValueError("invalid validation truth") + ids = _ids(truth) + if len(ids) != len(truth["y"]): + raise ValueError("validation row mismatch") + _disjoint(train_ids, ids) + if app == "A6": + if truth["y"].ndim != 2 or not truth["y"].shape[1]: + raise ValueError("vector targets required") + primary = [f"rmse_{k}" for k in range(truth["y"].shape[1])] + else: + primary = PRIMARY[app] + weights = protocol["selection_weights"] + if not isinstance(weights, dict) or set(weights) != set(primary): + raise ValueError("all primary metrics required for selection") + if any(type(w) not in (int, float) or not np.isfinite(w) or w <= 0 for w in weights.values()): + raise ValueError("positive finite selection weights required") + if app == "A6": + targets = load(root, protocol["train_targets"]) + _fields(targets, "row_ids y") + if not np.array_equal(_ids(targets), train_ids): + raise ValueError("training target row mismatch") + if targets["y"].shape != (len(train_ids), len(primary)): + raise ValueError("training target shape mismatch") + frozen_scale = read_json(_bytes(root, protocol["target_scale"])) + expected_scale = fit_target_scale(targets["y"]) + if digest(frozen_scale) != digest(expected_scale): + raise ValueError("target scale differs from training population") + expected_weights = { + key: 1.0 / std for key, std in zip(primary, expected_scale["std"], strict=True) + } + if weights != expected_weights: + raise ValueError("selection weights differ from training scale") + methods = protocol["methods"] + if not isinstance(methods, dict) or not methods: + raise ValueError("missing methods") + expected = {} + for method, configs in methods.items(): + if not isinstance(method, str) or not re.fullmatch(r"[a-zA-Z0-9_-]+", method): + raise ValueError("invalid method ID") + if ( + not isinstance(configs, list) + or len(configs) != 16 + or any(not isinstance(c, dict) or not c for c in configs) + ): + raise ValueError("exactly 16 configurations per method required") + if len({digest(c) for c in configs}) != 16: + raise ValueError("duplicate configurations") + expected.update({f"{method}:{i:02}": config for i, config in enumerate(configs)}) + if not isinstance(records, list) or len(records) != len(expected): + raise ValueError("missing search trials") + scores, artifacts = {}, {} + for record in records: + _fields(record, "id config status exit_code protocol_sha256 prediction model log") + trial = record["id"] + if trial not in expected or trial in scores: + raise ValueError("unknown or duplicate trial") + if ( + digest(record["config"]) != digest(expected[trial]) + or record["protocol_sha256"] != pinned_protocol_sha256 + ): + raise ValueError("changed trial configuration/protocol") + if ( + record["status"] != "pass" + or type(record["exit_code"]) is not int + or record["exit_code"] != 0 + ): + raise ValueError("failed search trial") + _bytes(root, record["log"]) + _bytes(root, record["model"]) + prediction = load(root, record["prediction"]) + _fields(prediction, "row_ids prediction") + if not np.array_equal(ids, prediction["row_ids"]): + raise ValueError("validation prediction row mismatch") + measured = metrics( + app, + truth["y"], + prediction["prediction"], + row_ids=ids, + **{k: v for k, v in truth.items() if k not in ["row_ids", "y"]}, + ) + # A6 is a mean of standardized errors, not normalized inverse-scale weights. + denominator = len(primary) if app == "A6" else sum(weights.values()) + value = sum(weights[k] * measured[k] for k in primary) / denominator + if not np.isfinite(value): + raise ValueError("invalid selection score") + scores[trial] = {"metrics": measured, "selection": value} + artifacts[trial] = {k: record[k] for k in ["model", "prediction", "log"]} + direction = -1 if app == "A4" else 1 + selected = min(scores, key=lambda t: (direction * scores[t]["selection"], t)) + return dict( + schema="openboost-selection-receipt-v1", + protocol_sha256=pinned_protocol_sha256, + records_sha256=digest(sorted(records, key=lambda r: r["id"])), + selected=selected, + scores=scores, + artifacts=artifacts, + ) + + +def seal(receipt, path): + """Write once; the orchestrator retains the returned digest independently.""" + raw = json.dumps(receipt, sort_keys=True, indent=2, allow_nan=False).encode() + b"\n" + with Path(path).open("xb") as f: + f.write(raw) + f.flush() + __import__("os").fsync(f.fileno()) + return hashlib.sha256(raw).hexdigest() + + +def release_test( + protocol, records, receipt_path, directory, pinned_protocol_sha256, pinned_receipt_sha256 +): + """Return test features and the selected model descriptor only after re-audit. + + Does not deserialize models or read test labels. External workers must verify + the returned model hash when loading and enforce process access restrictions. + """ + _hash(pinned_receipt_sha256) + raw = Path(receipt_path).read_bytes() + if hashlib.sha256(raw).hexdigest() != pinned_receipt_sha256: + raise ValueError("changed receipt") + receipt = read_json(raw) + fresh = audit(protocol, records, directory, pinned_protocol_sha256) + if digest(receipt) != digest(fresh): + raise ValueError("receipt does not match independent selection") + root = Path(directory).resolve() + features = load(root, protocol["test_features"]) + if not {"row_ids", "x"} <= set(features) or set(features) - { + "row_ids", + "x", + "exposure", + "query", + }: + raise ValueError("test features must exclude targets") + ids = _ids(features) + if ( + features["x"].ndim != 2 + or len(features["x"]) != len(ids) + or not np.isfinite(features["x"]).all() + ): + raise ValueError("finite encoded test inputs required") + for field in ["exposure", "query"]: + if field in features and features[field].shape != ids.shape: + raise ValueError("unaligned test metadata") + if "exposure" in features and ( + not np.isfinite(features["exposure"]).all() or np.any(features["exposure"] <= 0) + ): + raise ValueError("invalid test exposure") + for partition in ["train_rows", "validation"]: + _disjoint(ids, _ids(load(root, protocol[partition]))) + return features, fresh["artifacts"][fresh["selected"]]["model"] diff --git a/benchmarks/v1/selection_smoke.py b/benchmarks/v1/selection_smoke.py new file mode 100644 index 0000000..3458957 --- /dev/null +++ b/benchmarks/v1/selection_smoke.py @@ -0,0 +1,188 @@ +"""Execute a synthetic 16-trial CPU search and sealed test release end to end. + +Run with the pinned baseline interpreter. Outputs are synthetic harness evidence, +not a preregistered real-data search or an OpenBoost quality/performance result. +""" + +import argparse +import hashlib +import importlib.metadata +import json +import platform +import subprocess +import sys +from pathlib import Path + +import numpy as np + +from benchmarks.v1.process_runner import execute +from benchmarks.v1.quality import metrics +from benchmarks.v1.selection import audit, digest, release_test, seal + + +def run(directory, patience=None): + root = Path(directory).resolve() + root.mkdir(parents=True, exist_ok=True) + if any(root.iterdir()): + raise ValueError("fresh smoke directory required") + repo = Path(__file__).resolve().parents[2] + rng = np.random.default_rng(73) + x = rng.normal(size=(96, 3)) + y = x[:, 0] - 0.3 * x[:, 1] + np.savez(root / "train-rows.npz", row_ids=np.arange(64)) + np.savez(root / "validation.npz", row_ids=np.arange(64, 80), y=y[64:80]) + np.savez(root / "test-features.npz", row_ids=np.arange(80, 96), x=x[80:]) + np.savez( + root / "worker-input.npz", + x_train=x[:64], + y_train=y[:64], + x_validation=x[64:80], + validation_row_ids=np.arange(64, 80), + ) + + def entry(path): + return { + "path": str(path.relative_to(root)), + "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + } + + if patience is not None: + with np.load(root / "worker-input.npz", allow_pickle=False) as d: + arrays = {k: d[k] for k in d.files} + np.savez(root / "worker-input.npz", **arrays, y_validation=y[64:80]) + + versions = {d.metadata["Name"]: d.version for d in importlib.metadata.distributions()} + sources = { + p.name: hashlib.sha256(p.read_bytes()).hexdigest() + for p in [ + Path(__file__), + repo / "benchmarks/v1/baseline_worker.py", + repo / "benchmarks/v1/selection.py", + repo / "benchmarks/v1/process_runner.py", + repo / "benchmarks/v1/quality.py", + ] + } + configs = [dict(rounds=i, learning_rate=0.1, max_depth=2, reg_lambda=1.0) for i in range(1, 17)] + protocol = dict( + schema="openboost-selection-v1", + application="A1", + fold=0, + identity=dict( + code=digest(sources), + data=digest(dict(seed=73, rows=96, features=3)), + split=digest([64, 80, 96]), + preprocessing=digest("identity numeric"), + environment=digest(versions), + search_design=digest({"configs": configs, "early_stopping_rounds": patience}), + ), + train_rows=entry(root / "train-rows.npz"), + validation=entry(root / "validation.npz"), + test_features=entry(root / "test-features.npz"), + selection_weights={"rmse": 1.0}, + methods={"xgboost": configs}, + ) + pinned_protocol = digest(protocol) + (root / "protocol.json").write_text(json.dumps(protocol, indent=2) + "\n") + records = [] + for i, config in enumerate(configs): + trial = f"xgboost:{i:02}" + job = dict( + application="A1", + library="xgboost", + device="cpu", + seed=73, + threads=2, + config=config, + input_npz=str(root / "worker-input.npz"), + ) + if patience is not None: + job["early_stopping_rounds"] = patience + job_path = root / f"job-{i}.json" + job_path.write_text(json.dumps(job)) + out = root / f"trial-{i}" + result = execute( + [sys.executable, str(repo / "benchmarks/v1/baseline_worker.py"), str(job_path)], + out, + timeout_s=60, + threads=2, + ) + if result["status"] != "pass": + raise RuntimeError(f"{trial}: {result['reason']}; see {out / 'worker.log'}") + records.append( + dict( + id=trial, + config=config, + protocol_sha256=pinned_protocol, + status=result["status"], + exit_code=result["exit_code"], + prediction=entry(out / "predictions.npz"), + model=entry(out / "model.bin"), + log=entry(out / "execution.json"), + ) + ) + (root / "records.json").write_text(json.dumps(records, indent=2) + "\n") + receipt = audit(protocol, records, root, pinned_protocol) + receipt_hash = seal(receipt, root / "receipt.json") + features, selected_model = release_test( + protocol, records, root / "receipt.json", root, pinned_protocol, receipt_hash + ) + # This downstream process is invoked only after release and gets one model. + # The runner never gives the validation workers the test-feature path. + code = """import hashlib,pickle,sys +from pathlib import Path +import numpy as np +sys.path.insert(0,sys.argv[1]) +from benchmarks.v1.baseline_worker import predict_saved +raw=Path(sys.argv[2]).read_bytes() +if hashlib.sha256(raw).hexdigest()!=sys.argv[3]: raise ValueError("model hash changed") +with np.load(sys.argv[4],allow_pickle=False) as d: + p=predict_saved(pickle.loads(raw),d["x"]) + np.savez(sys.argv[5],row_ids=d["row_ids"],prediction=p) +""" + subprocess.run( + [ + sys.executable, + "-c", + code, + str(repo), + str(root / selected_model["path"]), + selected_model["sha256"], + str(root / "test-features.npz"), + str(root / "test-predictions.npz"), + ], + check=True, + timeout=60, + ) + with np.load(root / "test-predictions.npz", allow_pickle=False) as d: + np.testing.assert_array_equal(d["row_ids"], features["row_ids"]) + score = metrics("A1", y[80:], d["prediction"]) + result = dict( + scope="synthetic execution smoke only; no E3 or performance acceptance", + status="pass", + source_sha=subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=repo, text=True + ).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=repo)), + source_hashes=sources, + packages=versions, + python=platform.python_version(), + os=platform.platform(), + threads=2, + seed=73, + trials=16, + early_stopping_rounds=patience, + selected=receipt["selected"], + protocol_sha256=pinned_protocol, + receipt_sha256=receipt_hash, + test_metrics=score, + ) + (root / "summary.json").write_text(json.dumps(result, indent=2) + "\n") + return result + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + parser.add_argument("--early-stopping-rounds", type=int) + args = parser.parse_args() + print(json.dumps(run(args.directory, args.early_stopping_rounds), indent=2)) diff --git a/benchmarks/v1/worker_data.py b/benchmarks/v1/worker_data.py new file mode 100644 index 0000000..c272841 --- /dev/null +++ b/benchmarks/v1/worker_data.py @@ -0,0 +1,334 @@ +"""Bind supported frozen real-data folds to numeric worker packets. + +Run as an evaluation-side preparer. Separate files are not an OS access boundary. +""" + +import argparse +import hashlib +import json +from pathlib import Path + +import numpy as np + +from benchmarks.v1 import adult, bike, housing, real_data +from benchmarks.v1.preprocessing import censoring_support, fit_encoder, fit_target_scale, transform + +NAMES = { + "A1": "housing", + "A2": "adult", + "A3": "covertype", + "A5": "bike", + "A11": "housing", + "A6": "parkinsons", + "A7": "insurance", + "A8": "insurance", + "A9": "insurance", + "A10": "veteran", + "A12": "concrete", +} +PARTS = ("train", "validation", "test") + + +def insurance_population(application, raw, splits): + """Map policy partitions to the exact preregistered application population.""" + if application not in ["A7", "A8", "A9"]: + raise ValueError("unsupported insurance application") + if application == "A8": + rows = raw["severity_policy_row"] + target = raw["severity_y"] + name = "insurance_severity" + elif application == "A9": + rows = np.flatnonzero(raw["aggregate_eligible"]) + target = raw["paid_total"][rows] / raw["exposure"][rows] + name = "insurance_aggregate" + else: + rows = np.arange(len(raw["y"])) + target = raw["y"] + name = "insurance" + data = dict( + x=raw["x"][rows], + y=target, + group=raw["group"][rows], + row_ids=np.arange(len(rows)) if application == "A8" else raw["group"][rows], + categories={k[9:]: v[rows] for k, v in raw.items() if k.startswith("category_")}, + ) + if application in ["A7", "A9"]: + data["exposure"] = raw["exposure"][rows] + if application == "A9": + data["paid_total"] = raw["paid_total"][rows] + mapped = [tuple(np.flatnonzero(np.isin(rows, part)) for part in split) for split in splits] + return data, mapped, name + + +def bind(application, data, parts, frozen): + """Check source-aligned partitions and training metadata before assembling arrays.""" + if application not in NAMES or set(parts) != set(PARTS): + raise ValueError("unsupported application or partitions") + x, y = data["x"], data["y"] + if len(x) != len(y) or not np.isfinite(y).all(): + raise ValueError("invalid targets") + for rows in parts.values(): + if rows.ndim != 1 or rows.dtype.kind not in "iu" or not len(rows): + raise ValueError("nonempty integer row indices required") + joined = np.concatenate(list(parts.values())) + if application == "A5": + if not np.array_equal(joined, np.arange(len(joined))) or len(joined) > len(x): + raise ValueError("rolling partitions must be an ordered source prefix") + dates = data["dates"] + if dates.shape != (len(x),) or np.any(dates[1:] < dates[:-1]): + raise ValueError("chronological dates required") + if any( + dates[parts[a][-1]] >= dates[parts[b][0]] + for a, b in zip(PARTS, PARTS[1:], strict=False) + ): + raise ValueError("dates cross rolling boundaries") + elif not np.array_equal(np.sort(joined), np.arange(len(x))): + raise ValueError("partitions must cover each source row exactly once") + ids = data.get("row_ids", np.arange(len(x))) + if ids.shape != (len(x),) or ids.dtype.kind not in "iuUS" or len(np.unique(ids)) != len(ids): + raise ValueError("unique source row identifiers required") + categories = data.get("categories", {}) + train = parts["train"] + if "group" in data: + groups = [set(data["group"][parts[p]].tolist()) for p in PARTS] + if any(groups[i] & groups[j] for i in range(3) for j in range(i)): + raise ValueError("group crosses partitions") + if fit_encoder(x[train], {k: v[train] for k, v in categories.items()}) != frozen["encoder"]: + raise ValueError("training encoder differs from freeze") + metadata = {} + if application == "A6": + metadata["target_scale"] = fit_target_scale(y[train]) + if metadata["target_scale"] != frozen["target_scale"]: + raise ValueError("training target scale differs from freeze") + if application in ["A7", "A8", "A9"]: + if y.ndim != 1 or np.any(y < 0) or (application == "A8" and np.any(y <= 0)): + raise ValueError("invalid count/amount targets") + if application == "A7" and np.any(y != np.floor(y)): + raise ValueError("integer counts required") + if application in ["A7", "A9"]: + exposure = data["exposure"] + if exposure.shape != y.shape or not np.isfinite(exposure).all() or np.any(exposure <= 0): + raise ValueError("positive finite exposure required") + if application == "A9" and not np.allclose( + y * exposure, data["paid_total"], rtol=1e-12, atol=1e-8 + ): + raise ValueError("annualized targets do not reconstruct period totals") + if application == "A10": + event = data["event"] + if event.shape != y.shape or not np.isin(event, [0, 1]).all() or np.any(y <= 0): + raise ValueError("invalid censoring targets") + metadata["censoring_support"] = censoring_support(y[train], event[train]) + if metadata["censoring_support"] != frozen["censoring_support"]: + raise ValueError("training censoring support differs from freeze") + encoded = {} + for part, rows in parts.items(): + result = transform(frozen["encoder"], x[rows], {k: v[rows] for k, v in categories.items()}) + expected = frozen["partitions"][part] + if ( + real_data.array_hash(rows) != expected["rows_sha256"] + or real_data.array_hash(result) != expected["x_sha256"] + or list(result.shape) != expected["shape"] + ): + raise ValueError("partition differs from preprocessing freeze") + encoded[part] = result + if application == "A12": + age = data["structure"] + if age.shape != (len(x),) or not np.isfinite(age).all() or np.any(age <= 0): + raise ValueError("positive finite structure age required") + # Ordinary GBDT receives age as a feature; formula learners have a separate packet. + encoded = {p: np.column_stack([v, age[parts[p]]]) for p, v in encoded.items()} + metadata["age_train_range"] = [float(age[train].min()), float(age[train].max())] + worker = dict( + x_train=encoded["train"], + y_train=y[train], + x_validation=encoded["validation"], + y_validation=y[parts["validation"]], + validation_row_ids=ids[parts["validation"]], + ) + packets = { + "worker-input": worker, + "train-rows": {"row_ids": ids[train]}, + "validation": {"row_ids": ids[parts["validation"]], "y": y[parts["validation"]]}, + "test-features": {"row_ids": ids[parts["test"]], "x": encoded["test"]}, + "test-truth": {"row_ids": ids[parts["test"]], "y": y[parts["test"]]}, + } + if application == "A7": + worker.update( + exposure_train=exposure[train], exposure_validation=exposure[parts["validation"]] + ) + packets["test-features"]["exposure"] = exposure[parts["test"]] + metadata["target_units"] = "period count; exposure is an offset, unit training weights" + if application == "A9": + worker.update(weight_train=exposure[train], weight_validation=exposure[parts["validation"]]) + packets["validation"]["weight"] = exposure[parts["validation"]] + packets["test-truth"]["weight"] = exposure[parts["test"]] + metadata["target_units"] = "annualized paid total; exposure weight once, no offset" + for part in ("validation", "test"): + packets[part + "-period"] = dict( + row_ids=ids[parts[part]], + exposure=exposure[parts[part]], + paid_total=data["paid_total"][parts[part]], + ) + if application == "A8": + metadata["row_identity"] = ( + "zero-based retained positive joined claim position; group is policy ID" + ) + if application == "A10": + worker.update(event_train=event[train], event_validation=event[parts["validation"]]) + packets["validation"]["event"] = event[parts["validation"]] + packets["test-truth"]["event"] = event[parts["test"]] + if application == "A12": + for part in ("validation", "test"): + packets[part + "-structure"] = dict( + row_ids=ids[parts[part]], + age=age[parts[part]], + train_min=np.asarray(metadata["age_train_range"][0]), + train_max=np.asarray(metadata["age_train_range"][1]), + ) + return packets, metadata + + +def export( + application, + directory, + *, + root=Path("build/v1-data"), + housing_path=Path("build/foundation_data/cal_housing.tgz"), + adult_path=Path("/tmp/openboost-v1-adult.zip"), + bike_path=Path("/tmp/openboost-v1-bike.zip"), +): + if application not in NAMES: + raise ValueError("unsupported application") + directory = Path(directory) + directory.mkdir(parents=True, exist_ok=True) + if any(directory.iterdir()): + raise ValueError("fresh output directory required") + source_dir = Path(__file__).parent / "datasets" + name = NAMES[application] + prep_path = source_dir / "preprocessing.json" + prep = json.loads(prep_path.read_text()) + # Refuse to silently reinterpret a freeze with changed reader/preprocessing code. + for filename, digest in prep["source_files"].items(): + if hashlib.sha256(Path(__file__).with_name(filename).read_bytes()).hexdigest() != digest: + raise ValueError("frozen source implementation changed") + source_path = source_dir / (name + ".json") + source = json.loads(source_path.read_text()) + if name == "adult": + first, last = adult.load_archive(adult_path) + for member, rows in [("adult.data", first), ("adult.test", last)]: + if ( + adult.digest(adult.canonical(rows)) + != source["source_records"][member]["data_sha256"] + ): + raise ValueError("Adult records differ from freeze") + raw = np.asarray(first["x"] + last["x"], dtype=object) + numeric = [i for i, c in enumerate(adult.FEATURES) if c in adult.NUMERIC] + data = dict( + x=raw[:, numeric].astype(float), + y=np.asarray(first["y"] + last["y"]), + row_ids=np.asarray(first["row_ids"] + last["row_ids"]), + categories={ + c: raw[:, i] for i, c in enumerate(adult.FEATURES) if c not in adult.NUMERIC + }, + ) + splits = [ + ( + *adult.stratified_split(first["y"], seed), + np.arange(len(first["y"]), len(data["y"]), dtype=" 0) + if app == "A10": + np.testing.assert_array_equal(predictions["prediction"][:, 1], 1) + if app in ["A2", "A3"]: + assert np.all( + (predictions["prediction"] >= 0) & (predictions["prediction"] <= 1) + ) + if app == "A3": + np.testing.assert_allclose( + predictions["prediction"].sum(axis=1), 1, atol=1e-6 + ) + training = json.loads((output / "training.json").read_text()) + if app == "A6": + assert training["target_scale"] == fold["metadata"]["target_scale"] + record["training"] = training + result["cells"].append(record) + (root / "summary.json").write_text(json.dumps(result, indent=2) + "\n") + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path) + parser.add_argument("--applications", nargs="+", choices=NAMES) + args = parser.parse_args() + result = run(args.directory, args.applications) + print( + { + "cells": len(result["cells"]), + "passed": sum(c["status"] == "pass" for c in result["cells"]), + } + ) + if any(c["status"] != "pass" for c in result["cells"]): + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/v1/worker_smoke.py b/benchmarks/v1/worker_smoke.py new file mode 100644 index 0000000..10c212a --- /dev/null +++ b/benchmarks/v1/worker_smoke.py @@ -0,0 +1,150 @@ +"""Synthetic baseline worker persistence and external exposure checks.""" + +import hashlib +import importlib.metadata +import json +import pickle +import platform +import subprocess +import sys +import tempfile +from pathlib import Path + +import numpy as np + +from benchmarks.v1.baseline_worker import fit, predict_saved + + +def run(patience=None): + rng = np.random.default_rng(41) + x = rng.normal(size=(96, 5)) + rows = [] + configs = { + "xgboost": {"max_depth": 2, "reg_lambda": 1.0}, + "lightgbm": {"num_leaves": 4, "lambda_l2": 1.0}, + "catboost": {"depth": 2, "l2_leaf_reg": 1.0}, + "ngboost": {"weak_depth": 2, "minibatch_frac": 1.0}, + } + for library, config in configs.items(): + for number in range(1, 13): + task = f"A{number}" + if task == "A4" or (library == "ngboost" and task != "A11"): + continue + if ( + (library == "xgboost" and task == "A11") + or (library == "lightgbm" and task in ["A10", "A11"]) + or (library == "catboost" and task == "A8") + ): + continue + y = x[:, 0] + 0.1 * rng.normal(size=96) + if task == "A2": + y = (y > 0).astype(int) + elif task == "A3": + y = np.arange(96) % 3 + elif task == "A6": + y = np.column_stack([100 + 20 * y, -300 + 0.01 * x[:, 1], np.full(96, 7.0)]) + elif task in ["A7", "A8", "A9", "A10"]: + y = np.exp(y) + if task == "A7": + y = np.round(y) + arrays = dict( + x_train=x[:72], + y_train=y[:72], + x_validation=x[72:], + validation_row_ids=np.arange(72, 96), + weight_train=np.linspace(0.5, 2, 72), + exposure_train=np.linspace(0.2, 1, 72), + exposure_validation=np.linspace(0.3, 1, 24), + event_train=np.arange(72) % 3 != 0, + ) + if task != "A7": + arrays.pop("exposure_train") + arrays.pop("exposure_validation") + if task != "A10": + arrays.pop("event_train") + job = dict( + application=task, + library=library, + seed=41, + threads=2, + device="cpu", + classes=3, + config=dict(rounds=4, learning_rate=0.1, **config), + ) + if patience is not None: + job["early_stopping_rounds"] = patience + job["config"]["rounds"] = 24 + arrays["y_validation"] = ( + -y[72:] if task in ["A1", "A12"] or library == "ngboost" else y[72:] + ) + arrays["weight_validation"] = np.linspace(0.5, 2, 24) + if task == "A10": + arrays["event_validation"] = np.arange(24) % 3 != 0 + prediction, saved = fit(job, arrays) + reloaded = pickle.loads(pickle.dumps(saved)) + replay = predict_saved(reloaded, x[72:], arrays.get("exposure_validation")) + np.testing.assert_allclose(prediction, replay, rtol=1e-7, atol=1e-8) + record = dict( + library=library, + application=task, + status="pass", + shape=list(prediction.shape), + reload_max_abs_error=float(np.max(np.abs(prediction - replay))), + ) + if task == "A6": + scale = saved["target_scale"] + np.testing.assert_allclose(scale["mean"], y[:72].mean(axis=0)) + np.testing.assert_allclose(scale["std"][:2], y[:72].std(axis=0)[:2]) + assert scale["std"][2] == 1 and scale["constant"] == [False, False, True] + np.testing.assert_allclose(prediction[:, 2], 7.0) + np.testing.assert_allclose(arrays["y_train"], y[:72]) + record["target_scale"] = scale + if patience is not None: + record["stopping"] = saved["stopping"] + for stop in saved["stopping"]: + history = stop["history"] + validation = history.get("validation", history.get("val")) + values = next(iter(validation.values())) + assert stop["selected_rounds"] == int(np.argmin(values)) + 1 + if task == "A1" or library == "ngboost": + assert stop["selected_rounds"] < len(values) < 24 + if patience is not None and (task in ["A1", "A6"] or library == "ngboost"): + with tempfile.TemporaryDirectory() as temp: + folder = Path(temp) + (folder / "model.pkl").write_bytes(pickle.dumps(saved)) + np.save(folder / "x.npy", x[72:]) + code = "import pickle,sys,numpy as np; from pathlib import Path; from benchmarks.v1.baseline_worker import predict_saved; p=Path(sys.argv[1]); m=pickle.loads((p/'model.pkl').read_bytes()); np.save(p/'prediction.npy',predict_saved(m,np.load(p/'x.npy')))" + subprocess.run([sys.executable, "-c", code, temp], check=True, timeout=60) + new_process = np.load(folder / "prediction.npy") + np.testing.assert_allclose(prediction, new_process, rtol=1e-7, atol=1e-8) + record["new_process_max_abs_error"] = float( + np.max(np.abs(prediction - new_process)) + ) + if task == "A7": + doubled = predict_saved(reloaded, x[72:], 2 * arrays["exposure_validation"]) + np.testing.assert_allclose(doubled, 2 * replay, rtol=1e-6, atol=1e-7) + record["exposure_doubling_max_abs_error"] = float( + np.max(np.abs(doubled - 2 * replay)) + ) + rows.append(record) + return rows + + +if __name__ == "__main__": + result = dict( + scope="synthetic numeric fixed-round baseline worker checks, not real quality", + cells=run(), + seed=41, + threads=2, + python=platform.python_version(), + os=platform.platform(), + packages={d.metadata["Name"]: d.version for d in importlib.metadata.distributions()}, + source_sha=subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip(), + dirty=bool(subprocess.check_output(["git", "status", "--porcelain"])), + source_hashes={ + name: hashlib.sha256(Path(__file__).with_name(name).read_bytes()).hexdigest() + for name in ["worker_smoke.py", "baseline_worker.py"] + }, + ) + Path("benchmarks/v1/evidence/worker-cpu.json").write_text(json.dumps(result, indent=2) + "\n") + print(result["cells"]) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7ee2f45 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,6 @@ +# Documentation scope + +The current documentation source is `docs/v1/`, selected by `mkdocs.yml`. +Other files in this directory document the retired implementation at revision +`50acfc6`; they remain as historical material, not current capability claims. +The new component and recipe documentation will be added alongside implementation. diff --git a/docs/benchmarks.md b/docs/benchmarks.md index 35b9b44..f54a622 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -1,134 +1,60 @@ # Benchmarks -Three gates. All numbers below are from committed Modal runs (A100 for -speed and capability, CPU for UCI quality). This page is the source of -truth that the README summarizes. +Benchmark scripts and verified results are separate deliverables. The remote +integration brought speed, UCI quality, FormulaBoost, and survival benchmark +harnesses, but their previously transcribed tables do not have corresponding +committed raw reports with complete provenance in this checkout. Those tables +are withheld pending reproducible artifacts; they are not release gates passed. -## Speed: NaturalBoost vs NGBoost +## Available evidence -Heteroscedastic Normal, 80 features, 500 trees, learning rate 0.03, -depth 3. OpenBoost on a Modal A100; NGBoost on CPU (it has no GPU). +The older CPU comparison is committed at +[ngboost_comparison_20260720.json](https://github.com/jxucoder/openboost/blob/504fdd0bfc60e5d8e7518250e087fb7e4766d1b4/benchmarks/results/ngboost_comparison_20260720.json). +It covers its recorded datasets, seed, and configuration only. It cannot +support a GPU speed claim or full-suite quality claim. -| n_train | OpenBoost (A100) | NGBoost (CPU) | speedup | OB NLL | NGB NLL | cov90 | -|--------:|-----------------:|--------------:|--------:|-------:|--------:|------:| -| 45,000 | 3.01s | 1413.93s | 470× | 2.107 | 2.105 | 0.884 | -| 90,000 | 2.21s | 2715.58s | **1229×** | 2.108 | 2.102 | 0.899 | -| 450,000 | 5.40s | n/a | n/a | 2.100 | n/a | 0.901 | -| 900,000 | 6.94s | n/a | n/a | 2.104 | n/a | 0.902 | +The ScoringBench integration in `benchmarks/scoringbench/` keeps official +quality runs separate from OpenBoost's large-sample extension. Neither the +presence of that integration nor passing unit tests implies accepted results +on an external leaderboard. -NGBoost was not run past 100K (45 minutes at 90K). Quality is tied at -every size both finished. Linear-scale extrapolation of NGBoost to 900K -is ~hours; we do not quote that as a measured speedup. +## Reproduce candidate comparisons -**Honest reading.** The 1229× is GPU OpenBoost vs CPU NGBoost, because -that is the product comparison. On CPU the two libraries are ~parity -(0.8–1.3× wall-clock, NLL/CRPS/RMSE within ~1%) on the older committed -CPU comparison (`benchmarks/results/ngboost_comparison_20260720.json`). +These commands run experiments; execution alone does not establish a result. ```bash -# A100 speed + quality uv run modal run benchmarks/bench_probabilistic.py --suite speed uv run modal run benchmarks/bench_probabilistic.py::quality - -# CPU-only NGBoost comparison (no GPU) -OPENBOOST_BACKEND=cpu uv run --with ngboost python benchmarks/bench_ngboost_comparison.py -``` - -## Quality: UCI vs NGBoost - -NGBoost-paper UCI datasets, 20 paired 80/20 splits, shared 500-tree -budget + patience-50 early stopping on a common val set. NLL, lower is -better. `delta = OB − NGB` (negative = OpenBoost better). `p` is a paired -Wilcoxon. - -| dataset | OB NLL | NGB NLL | delta | p | cov90 | -|---|--:|--:|--:|--:|------:| -| boston | 2.679 | 2.639 | +0.040 | 0.57 | 0.894 | -| concrete | 3.128 | 3.135 | −0.007 | 0.60 | 0.848 | -| energy | 1.694 | 1.701 | −0.007 | 0.09 | 0.911 | -| kin8nm | −0.430 | −0.400 | −0.031 | **2e-6** | 0.851 | -| protein | 1.930 | 1.943 | −0.013 | **0.002** | 0.940 | -| wine | 1.028 | 1.031 | −0.003 | 0.13 | 0.879 | -| yacht | 0.814 | 0.828 | −0.014 | 0.73 | 0.877 | -| california | 0.584 | 0.596 | −0.011 | **2e-6** | 0.900 | - -Tied-or-better on **8 of the 11** datasets in the suite. Significant wins -on kin8nm, protein, california; no significant loss. boston +0.04 is not -significant. 90% coverage lands in 0.85–0.94. - -**Coverage of the suite is incomplete.** `naval_propulsion_plant`, -`power`, and `YearPredictionMSD` are **unmeasured**, not neutral: they -dropped out during an OpenML outage (naval's `data_id` 44898 is a -deactivated version; the name endpoint was returning 503). The rerun that -would fill them in has not been completed, so "tied-or-better" covers 8 -datasets and says nothing about the other 3. california is from a local -baseline run, because Modal's figshare egress returned 403. - -```bash -uv run modal run benchmarks/bench_probabilistic.py::quality -``` - -## Capability: FormulaBoost - -Sales curve `y = a(z) * x ** sigmoid(b(z) * x)`. Train `x ∈ [0.25, 2.5]`, -extrap `x ∈ [3, 5]` against the noiseless true curve. 300 rounds, depth 3, -lr 0.1. n = 200K: - -| | test RMSE | extrap RMSE | corr `b` | fit | -|---|--:|--:|--:|--:| -| FormulaBoost `full` | 0.139 | **0.183** | **0.877** | 17.1s | -| FormulaBoost `diag` | 0.130 | 0.181 | 0.730 | 12.0s | -| FormulaBoost `plain` | 1.410 | 3.931 | 0.652 | 10.3s | -| global `(a, b)` | 1.641 | 4.396 | n/a | n/a | -| black-box GBDT | 0.127 | 3.872 | n/a | 0.7s | -| XGBoost custom (diag Hess) | 0.129 | 0.203 | 0.599 | 19.9s | - -Gates (40K and 200K): extrap vs black-box ≥5× (measured **21×**), `full` -beats `plain`, `full` beats global, `full` not worse than XGBoost-diag. - -**Honest reading.** The claim that lands is extrapolation (the formula -constrains `x`) and recovery of `b(z)`. Full GGN's edge over diag is -parameter recovery, not test RMSE (`0.181` vs `0.183`). `plain` diverges, so -GGN is load-bearing. XGBoost's custom-objective API is diagonal-only, so -it cannot express the off-diagonal term that buys `b(z)`. - -```bash uv run modal run benchmarks/bench_formula.py +uv run modal run benchmarks/bench_survival.py +OPENBOOST_BACKEND=cpu uv run --with ngboost python benchmarks/bench_ngboost_comparison.py ``` -## Capability: Weibull AFT - -Both `λ(z)` and `k(z)` vary with covariates. ~35% right-censoring. 300 -rounds, depth 3. n = 200K: - -| | C-index | NLL | cov80 | shape corr | fit | -|---|--:|--:|--:|--:|--:| -| OpenBoost `WeibullAFT` | **0.680** | **0.761** | 0.803 | **0.997** | 5.4s | -| XGBoost `survival:aft` (`extreme`) | 0.672 | 0.830 | 0.852 | n/a (global `k = 1.34`) | 11.7s | -| global constant | 0.500 | 0.896 | 0.810 | n/a | n/a | +For NaturalBoost, compare held-out NLL, CRPS and interval coverage at matched +training budgets before discussing fit and prediction time. Record failed or +unavailable datasets rather than treating them as ties. -C-index is close (ranking follows scale). The NLL gap and the shape -correlation are the capability: XGBoost holds Weibull shape as one -hyperparameter. Coverage of the 80% interval is nearer the nominal 0.80. +For FormulaBoost, measure in-domain error, extrapolation error, and parameter +recovery separately. Compare full/diagonal/plain preconditioning, a global +formula fit, and appropriate tree baselines. A known synthetic formula is a +mechanism test, not proof of real-world extrapolation. -```bash -uv run modal run benchmarks/bench_survival.py -``` +For WeibullAFT, measure censored NLL, ranking, calibration and parameter +recovery separately. Report the censoring process and model assumptions. -## What we are not claiming +## Required artifacts -- A 3900× speedup at 1M rows. That is a linear extrapolation of NGBoost, - not a measurement. -- That FormulaBoost `full` wins in-sample RMSE. It does not, vs `diag`. -- That OpenBoost is a faster drop-in for XGBoost/LightGBM mean regression. - It is not; those are optimized C++. -- PGBM as a product competitor. It is a GPU probabilistic reference, not - a gate. -- Quality parity on the full UCI suite. Three datasets (naval, power, - YearPredictionMSD) never produced numbers; the claim is 8 of 11. +Each numerical claim needs a committed raw result recording source SHA and +dirty state, dataset/version/hash, splits/seeds, package versions, OS, +CPU/RAM/threads, GPU/driver/CUDA, exact commands, actual execution path, +fallbacks, and timing/compilation policy. Report repeated runs and failures. -## JSON artifacts +Measure end-to-end fit and prediction, including objective computation, +transfers, and synchronization. CPU/GPU comparisons must identify resource +differences. Do not extrapolate unmeasured timings or trade quality for speed +without stating the change. -Reports write to `benchmarks/results/` (gitignored locally; the numbers on -this page are transcribed from the Modal runs recorded in -`tasks/todo.md`). Re-run the commands above to refresh them. +The historical scripts write to ignored `benchmarks/results/` paths. A fresh +run must explicitly retain and review its raw files before any documentation +is updated with numbers. The foundation execution plan adds a dedicated +tracked evidence directory and stricter result handling. diff --git a/docs/cookbook/experimental-extensions.md b/docs/cookbook/experimental-extensions.md new file mode 100644 index 0000000..a616570 --- /dev/null +++ b/docs/cookbook/experimental-extensions.md @@ -0,0 +1,73 @@ +# Write an experimental extension + +Use `openboost.experimental` to prototype distributional objectives, leaf rules +and predetermined schedules in an independent package. The API is experimental; +keep the tested OpenBoost version in your package metadata. Start on CPU, then +verify the same math and final predictions on real CUDA hardware before declaring +CUDA support. The `examples/extensions/` packages and their standalone `demo.py` +are the executable reference. + +## Implement the contract + +| Extension | Implement | Required behavior | +|---|---|---| +| Objective | `channel_names`, `supported_devices`, `init_raw(y, sample_weight=None, extra=None)`, `step(raw, y, sample_weight=None, extra=None, *, context)`, `loss_value(...)`, `constrain(raw, extra=None)` | Initialize on CPU; return one contiguous float32 G/H pair per channel on `context.device`; weight both exactly once; curvature must be nonnegative. Reject unsupported extra targets. | +| Leaf rule | `supported_devices`, `values(G, H, *, config, context)` | Return finite contiguous float32 leaf values on the same device; zero empty/inactive/zero-G-and-H slots. Attach through `LevelWiseBuilder(leaf_rule=rule)`. | +| Schedule | `coefficients(round_idx, channel_names, base_learning_rate)` | Return all channel names with full finite nonnegative coefficients; the trainer applies them once. Use the supplied zero-based round, never global RNG state. | + +Use `context.xp` for vector arithmetic and `context.rng` for randomness. CPU +inputs are read-only; CUDA boundaries isolate and check borrowed arrays. Return +independent output buffers, never mutate inputs or reuse another channel's G/H. +Effective curvature may be Fisher curvature rather than the exact Hessian: +document the distinction and test it against an independent mathematical oracle. +`loss_value` returns a host scalar; `constrain` converts raw scores to parameters. + +Compose `Booster(objective=..., tree_builder=..., step_schedule=..., +config=TrainerConfig(...), device="cpu")`. Save with `model.save(...)`; a fresh +interpreter can call `Booster.load(...).predict_raw(X)` without training plugins. +The loaded model is inference-only. Preserve your parameter transformation +separately when applications need constrained parameters rather than raw scores. + +## Choose a supported path + +| Capability | CPU default builder | LevelWiseBuilder CPU / strict CUDA | +|---|---|---| +| Numeric nonmissing, L2, full sampling | Yes | Yes | +| Missing numeric values, L1, sampling | Yes | Rejected | +| Custom leaf rule through public interface | Select LevelWiseBuilder | Yes, with declared device support | +| Eval sets, callbacks, early stopping | Yes | CPU yes; strict CUDA rejected | +| Objective inputs during rounds | NumPy | NumPy / CuPy | +| Fit inputs and binning | Host | Host, including CUDA fits | +| Prediction after fit/load | CPU | CPU | +| Non-default CUDA stream | Not applicable | Rejected | +| Further training after load | Rejected | Rejected | + +Depth is 0–8 and regular bins are 2–254. The process-global backend does not +support concurrent mixed-backend fits. Categorical input is outside the numeric +experimental facade. `fallback="error"` is the default; `fallback="warn"` handles +unsupported capability by selecting the entire CPU fit and reporting the reason. +Runtime errors do not trigger a silent retry. Inspect `fit_report_` for execution. + +## Verify the package, then the result + +1. Test independent float64 loss/gradient/curvature references, nonunit and zero + weights, finite outputs, forbidden aliasing and explicit unsupported inputs. +2. Test at least two rounds: leaf/schedule changes must affect later gradients, + and CPU/CUDA final predictions and task metrics must agree within declared + tolerances. A skipped device test provides no CUDA evidence. +3. Build with `uv build --wheel`; install the core and plugin wheels into a fresh + `uv venv` outside the checkout. Use no editable install or private OpenBoost + imports. Run the standalone example under an `if __name__ == "__main__":` + guard, then uninstall plugins and verify CPU predictions in a new interpreter. + +Reproduce the repository's CPU check with +`uv run --no-sync python examples/extensions/verify_wheels.py /tmp/openboost-extension-evidence`. +The CUDA installation check uses `benchmarks.foundation.prepare --suite extensions` +and the `foundation_extensions` Modal entrypoint, as documented in the examples. + +A real failure shaped this workflow: top-level GPU training in the demo re-entered +when CUDA discovery spawned a worker. A main guard fixed the failure; the original +JUnit remains committed. Another checked failure is extreme Normal log scale +causing zero precision: the objective rejects it instead of accepting a zero +curvature. Unsupported eval in strict CUDA is a capability error, not a successful +GPU fit. See the [full API boundary](../user-guide/experimental.md) for details. diff --git a/docs/getting-started/gpu-setup.md b/docs/getting-started/gpu-setup.md index 63b04e6..af103a5 100644 --- a/docs/getting-started/gpu-setup.md +++ b/docs/getting-started/gpu-setup.md @@ -1,103 +1,87 @@ # GPU Setup OpenBoost uses CUDA for histogram building and tree construction. -NaturalBoost, FormulaBoost, and WeibullAFT share that tree path. Some -objective math still runs on the host (FormulaBoost GGN today; LogNormal / -digamma families). Trees are the expensive part at scale. +NaturalBoost, FormulaBoost, and WeibullAFT share a tree-building path. +FormulaBoost's finite-difference Jacobian and GGN solve run on CPU, as does +WeibullAFT's expected-Fisher step. Normal and Poisson objectives have device +kernels for eligible configurations. ## Verify detection +Install with `uv add --prerelease=allow "openboost[cuda]"`. For a repository +checkout use `uv sync --extra cuda --extra dev`. + ```python import openboost as ob - -print(ob.get_backend()) # "cuda" or "cpu" -print(ob.is_cuda()) # True if a GPU is active +print(ob.get_backend()) +print(ob.is_cuda()) ``` -Install the extra first: `pip install --pre "openboost[cuda]"`. +Detection confirms backend availability, not that every operation runs on GPU. +The native builder excludes missing values, categorical features, L1 +regularization, and row/column sampling in the shared trainer. Such inputs +may select another tree path. Check the actual model and configuration before +interpreting timing as a fully device-resident fit. ## Pin a backend ```python -import openboost as ob - -ob.set_backend("cpu") # debug / comparison +ob.set_backend("cpu") ob.set_backend("cuda") - -# Or: -# export OPENBOOST_BACKEND=cuda +with ob.backend_context("cpu"): + print(ob.get_backend()) ``` -`backend_context("cpu")` is a temporary switch that restores the previous -backend on exit. - -## What the A100 numbers actually are +Alternatively set `OPENBOOST_BACKEND=cpu` or `OPENBOOST_BACKEND=cuda`. +The backend is process-global; do not run mixed-backend fits concurrently in +one process. A context restores the previous backend on exit. -NaturalBoost vs NGBoost, heteroscedastic Normal, 80 features, 500 trees, -Modal A100. NGBoost has **no GPU implementation**, so this is GPU OpenBoost -against CPU NGBoost, which is the comparison that exists in the world. +## Measure the relevant workload -| n_train | OpenBoost (A100) | NGBoost (CPU) | speedup | -|--------:|-----------------:|--------------:|--------:| -| 45K | 3.01s | 1414s | 470× | -| 90K | 2.21s | 2716s | **1229×** | -| 450K | 5.40s | skipped (hours) | n/a | -| 900K | 6.94s | skipped | n/a | +GPU benefit depends on dataset shape, tree parameters, distribution, CUDA +stack, transfers, and JIT warm-up. A universal crossover size or speedup is +not established by backend detection. -NLL is tied at every size that both ran. Full tables: -[Benchmarks](../benchmarks.md). +1. Force the backend for each comparison. +2. Record first-use compilation separately from repeated warm timings. +3. Compare predictions and task metrics before runtime. +4. Measure end-to-end fit and prediction, peak memory, and failures. +5. Save raw results with source SHA, data/split, hardware, dependencies, + actual execution path, and exact commands. -!!! tip "When GPU helps" - Histogram trees win at tens of thousands of rows and up. Below ~5K - samples, kernel launch overhead often matches CPU. Use `float32` - features. Missing values and categoricals fall back from the - GPU-native builder to the hybrid path (warning emitted). +The ScoringBench integration under `benchmarks/scoringbench/` defines separate +official-quality and scale-extension protocols for probabilistic models. +Historical benchmark summaries do not replace reproducible raw artifacts. -On CPU, NaturalBoost and NGBoost are ~parity (0.8–1.3×). Do not quote the -A100 ratio as a CPU claim. +## Experimental multi-GPU -## FormulaBoost and WeibullAFT on GPU +The `distributed` extra installs Ray for experimental multi-GPU work. +Exact correctness and scaling evidence are still required before treating +that path as a supported production capability. NaturalBoost, FormulaBoost, +and WeibullAFT currently use one GPU. A single-GPU result does not validate +distributed training. -Both use GPU trees. FormulaBoost's GGN (finite-difference Jacobian + -`K×K` solve) currently runs on the host; at 200K rows full GGN still beat -an XGBoost custom objective (17s vs 20s on A100). WeibullAFT's expected -Fisher step is cheap (`2×2` per row). +## Requirements and troubleshooting -## Multi-GPU +- An NVIDIA GPU supported by the installed CUDA and Numba stack. +- A CUDA 12 runtime compatible with `cupy-cuda12x` from the CUDA extra. +- `numba-cuda>=0.23` and `cupy-cuda12x>=13` as specified by the package. -```python -import openboost as ob +If CUDA is not detected, inspect `nvidia-smi` and run: -model = ob.GradientBoosting(n_trees=100, n_gpus=4) -model.fit(X, y) - -model = ob.GradientBoosting(n_trees=100, devices=[0, 2]) -model.fit(X, y) +```bash +uv run python -c "from numba import cuda; print(cuda.is_available())" ``` -Requires `pip install --pre "openboost[distributed]"` (Ray). -NaturalBoost / FormulaBoost / WeibullAFT currently train on one GPU. - -## Requirements - -- NVIDIA GPU, CUDA Compute Capability 3.5+ -- CUDA Toolkit 11 or 12 -- `numba-cuda>=0.23` - -## Troubleshooting - -**Training seems slow on GPU.** Features should be `float32`. Tiny datasets -do not amortize kernel launch. Confirm `ob.is_cuda()` is True. - -**CUDA not detected.** - -1. `nvidia-smi` -2. `python -c "from numba import cuda; print(list(cuda.gpus))"` -3. Reinstall `openboost[cuda]` against the CUDA version on the machine +If training is slow, use float32 features, account for compilation and data +transfers, and inspect which objective/tree path actually executes. Report +CPU and GPU resources separately when comparing different libraries. -**Trained on GPU, loading on CPU.** Saved models are backend-agnostic. +Saved models store host tree state and support CPU inference. Verify a +prediction round trip for the model and feature types used in your workload: ```python model.save("model.joblib") -loaded = ob.NaturalBoostNormal.load("model.joblib") # CPU or GPU +with ob.backend_context("cpu"): + loaded = ob.NaturalBoostNormal.load("model.joblib") ``` diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index c7536eb..dafce1c 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -51,12 +51,13 @@ troubleshooting. |-------|-----------------|---------| | `cuda` | numba-cuda + CuPy for GPU trees | `pip install --pre "openboost[cuda]"` | | `sklearn` | scikit-learn wrappers | `pip install --pre "openboost[sklearn]"` | -| `jax` | autodiff for custom distributions / formulas | `pip install --pre "openboost[jax]"` | -| `distributed` | Ray for multi-GPU | `pip install --pre "openboost[distributed]"` | -| `all` | Everything | `pip install --pre "openboost[all]"` | +| `jax` | autodiff for custom distribution NLLs | `pip install --pre "openboost[jax]"` | +| `distributed` | Ray for experimental multi-GPU work | `pip install --pre "openboost[distributed]"` | +| `all` | Combined optional dependencies | `pip install --pre "openboost[all]"` | + +FormulaBoost currently uses finite-difference Jacobians on CPU. Installing +JAX does not switch its formula path to GPU autodiff. -Finite-difference Jacobians work without JAX. Install `jax` when you want -autodiff on a custom NLL. ## Requirements @@ -67,8 +68,8 @@ autodiff on a custom NLL. ### For GPU -- NVIDIA GPU, CUDA Compute Capability 3.5+ -- CUDA Toolkit 11 or 12 +- NVIDIA GPU supported by the installed CUDA/Numba stack +- CUDA 12 runtime compatible with the `cupy-cuda12x` extra - `numba-cuda>=0.23`, `cupy-cuda12x>=13` ## Verify diff --git a/docs/index.md b/docs/index.md index e028cde..99b7e55 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,6 @@ +> **Historical implementation:** this page describes the retired pre-rebuild API. +> Reproduce at Git revision `50acfc6`; see the repository README and `v1-sprints/` for current v1 status. + # OpenBoost

diff --git a/docs/migration/from-xgboost.md b/docs/migration/from-xgboost.md index 02189d1..f69c1c6 100644 --- a/docs/migration/from-xgboost.md +++ b/docs/migration/from-xgboost.md @@ -254,16 +254,14 @@ lo, hi = model.predict_interval(X_test, alpha=0.1) samples = model.sample(X_test, n_samples=1000) ``` -On the UCI datasets measured so far, NLL is tied or better vs NGBoost, and on -an A100 NaturalBoost fits in seconds at sizes where CPU-only NGBoost needs -most of an hour. See [Benchmarks](../benchmarks.md) for the caveats. +Compare predictive quality and end-to-end cost on your workload using the +[benchmark evidence requirements](../benchmarks.md). ### 2. A formula with off-diagonal GGN -XGBoost custom objectives cannot represent the off-diagonal of `JᵀJ`. -That term is what recovers coupled parameters (`b(z)` on the sales curve: -corr 0.877 vs 0.599). Black-box XGBoost also cannot extrapolate in the -structural input `x`. FormulaBoost is ~21x better there. +FormulaBoost fits a user-specified formula with covariate-dependent parameters. +Its full GGN option retains cross-parameter terms. Test whether this helps +parameter recovery and extrapolation for the particular formula and data. ```python def sales(theta, x): @@ -289,9 +287,8 @@ params = model.predict_params(Z) # {scale, shape} s = model.predict_survival(Z, t=12.0) ``` -On a varying-shape DGP, shape correlation is 0.997; XGBoost has no -per-row `k` to correlate. Censored NLL is better (0.761 vs 0.830); -C-index is close (0.680 vs 0.672). +Evaluate shape recovery, censored NLL and ranking separately on a process +with known parameters before applying the model to domain data. ### 4. A native Python custom loss (point-estimate) diff --git a/docs/user-guide/experimental.md b/docs/user-guide/experimental.md new file mode 100644 index 0000000..e6dcfda --- /dev/null +++ b/docs/user-guide/experimental.md @@ -0,0 +1,249 @@ +# Experimental extensions + +`openboost.experimental` is a small, evolving research API. Its `Booster` +uses the existing unified trainer. CPU supports the wider extension surface; +strict `device="cuda"` uses declared CUDA objectives and builders with resident +CuPy raw scores, targets, weights and updates. The built-in Normal/Poisson +adapter declares CUDA support; independent example packages provide NumPy/CuPy implementations. +Unsupported capability with `fallback="warn"` selects the entire CPU path before +training; runtime plugin/kernel failures raise and restore prior model state. + +An objective declares `channel_names` as a tuple of unique strings and +`supported_devices` as a frozenset. It implements `init_raw`, `step`, +`loss_value` and `constrain`. `step` receives the entire round-start raw state, +target and optional weights on the declared device, plus a frozen +`ExecutionContext` with device, NumPy/CuPy array module, per-fit generator, round index and channel (None for objectives). + +Each channel returns an independent `(grad, hess)` tuple of contiguous float32 +vectors. Gradient points toward increasing loss; Hessian is nonnegative +effective curvature, potentially a Fisher/preconditioner rather than an exact +second derivative. The objective applies sample weight to both arrays exactly +once; the trainer does not apply it again. `loss_value` should report the +weighted mean. All-zero, negative and non-finite weights are rejected. + +CPU inputs are read-only views; CUDA inputs are isolated device copies checked +for mutation. Output buffers must not alias inputs, each other, +or another channel's statistics; they remain valid until the round finishes. +These checks prevent accidental buffer reuse. CPU uses views; strict CUDA pays +for defensive device copies and validation. They do not sandbox hostile Python code. + +`DistributionObjectiveAdapter("normal", natural=True)` exposes existing +NaturalBoost objective math, including its unweighted initialization estimate. +`TrainerConfig` is shared with the internal trainer; it includes `random_state` +and `min_gain`. The initial CPU facade supports 0–8 tree depth, 2–254 bins, +numeric input including NaN, or a CPU `openboost.BinnedArray`. No new +performance or calibrated-quality guarantee follows from these interfaces. + +For a one-page implementation checklist and capability matrix, see +[Write an experimental extension](../cookbook/experimental-extensions.md). + +## Tree builders and updates + +Pass `tree_builder` and `step_schedule` explicitly to `Booster`. A builder +implements `build(binned, grad, hess, *, config, context)` and declares CPU +capability. Context now identifies the round and channel; the builder never +receives raw scores. It returns `BuiltTree(TreeStructure, train_prediction=None)`. +Only standard scalar package-owned trees are supported. Tree topology, values +and optional missing/categorical metadata are validated; compact state is +copied before storage so builder scratch buffers can be reused. If provided, +the cached prediction must exactly match tree prediction on training data. + +Explicit builders always take precedence over native eligibility. The current +`CPUHistogramBuilder` delegates splitting to the existing CPU core. Split gain +uses the unhalved score `G_L²/(H_L+lambda) + G_R²/(H_R+lambda) - G²/(H+lambda)` +(with L1 soft thresholding when requested), not half that value. The default +leaf is `-G/(H+lambda)`. Zero denominator and zero G yields a zero root leaf; +nonzero G fails. This adapter rejects the unverified combination +`reg_lambda=0, min_child_weight=0` before fitting; choose positive minimum child +weight for unregularized trees. It does not silently change those parameters. + +A schedule implements `coefficients(round_idx, channel_names, base_learning_rate)`. +It returns every channel's full finite, nonnegative coefficient. The trainer +applies each coefficient exactly once and stores it in `coefficients_` for +prediction/evaluation. `ConstantSchedule` returns the configured learning rate. +Use schedules rather than learning-rate-mutating callbacks. Only predetermined +per-round coefficients are supported, not line search or reweighting old trees. + +Save with `model.save("model.ob")` and load with `Booster.load("model.ob")`. +The saved state contains package-owned trees, binning metadata, base scores and +per-tree coefficients. Objective, builder and schedule objects are excluded. +Loading supports CPU `predict_raw` without installing the training plugins; +loaded models are inference-only. Create a fresh Booster for further training. +As with other OpenBoost persistence, load only trusted files (joblib). + +Early stopping restores trees and coefficients together. Checkpoints can be +loaded through the same inference API. A state without coefficients uses the +saved constant learning rate for each tree; this compatibility rule cannot +recover a missing nonconstant schedule. Unsupported experimental format versions +and old categorical states are rejected. Existing model persistence policies +are unchanged. + +## Fixed-slot histogram primitive + +`build_histograms(binned, grad, hess, sample_node_ids, active, +memory_budget_bytes=256 * 1024**2)` returns `HistogramBatch` with float32 +`grad`/`hess` arrays `(slots, features, 256)`, int32 `counts` `(slots,)`, and an +owned bool `active` mask. Inputs are contiguous NumPy arrays or CuPy arrays on +the current device. G/H already include objective weights: no second weighting +occurs. Counts include zero-weight rows. ID -1 excludes a row; inactive slots +ignore assigned rows. Empty slots are zero; bin 255 remains the missing bin. + +The limit is 511 slots. The budget covers returned arrays, excluding inputs +and transient device validation masks; allocation is rejected before creating +histograms if it would exceed the budget. CUDA uses the current CuPy stream, +retains aggregation results on device, and synchronizes scalar validation checks. +Floating-point CUDA accumulation order is not deterministic. No speed claim is +made. The primitives are composed by `LevelWiseBuilder` below and used by strict CUDA +extension sessions in the shared trainer. + +## Numeric split and routing primitives + +`find_splits(histograms, reg_lambda=1., min_child_weight=1., min_gain=0.)` +returns `SplitBatch`: int32 feature/threshold/left_child/right_child arrays, +float64 gain, and bool valid mask for each fixed slot. Prefix sums and scores +use float64 from the float32 histograms. Gain is the unhalved L2 score; it must +be positive and at least min_gain. Both children require positive H and at +least min_child_weight, including when min_child_weight=0. Exact ties select +the first feature, then the first threshold. Inactive, empty, terminal and +unsplittable slots have -1 indices, zero gain and valid=False. + +`partition(binned, sample_node_ids, splits)` returns a new int32 array of routed +IDs. It preserves -1 and nodes without a valid split. Children use fixed indices +`2*i+1` and `2*i+2`; routing never modifies the input IDs. Construct the next +active mask from valid children and call `build_histograms` on these routed IDs +to build actual child statistics. + +These operations accept the same contiguous NumPy/current-device CuPy boundary +as histograms. GPU arrays stay on device; scalar input checks synchronize. +Only numeric L2 splitting is supported. Histogram missing-bin G/H must be zero, +and routing rejects bin 255 even for zero-weight rows. Callers must provide +numeric bins; categorical metadata is outside this primitive API. `LevelWiseBuilder` +rejects missing/categorical inputs before growth. These primitives alone do not establish an end-to-end speedup. + +## Leaf reduction and custom rules + +`reduce_leaves(grad, hess, sample_node_ids, active)` returns `LeafStatistics`: +float32 grad/hess sums, int32 physical row counts, and an owned bool active mask, +all `(slots,)` on the input device. Inputs use the same contiguous array and +fixed-slot contract as histograms. Statistics are already weighted; zero-weight +rows still count. ID -1 and inactive assignments are excluded. + +`leaf_values(..., leaf_rule=None, config=None, context=None)` performs this +reduction and calls `rule.values(G, H, config=config, context=context)`. A rule +declares `supported_devices` and returns contiguous finite float32 `(slots,)` +on that device. Pass the fit's shared `ExecutionContext` when composing a +builder. Standalone calls create a context from the config seed. The rule gets +private compact G/H copies and a config copy; changing G/H is rejected. Returned +values are detached from plugin scratch. No sample-sized defensive copy occurs. + +`NewtonLeafRule` implements L2 `-G/(H+reg_lambda)`. Zero denominator with zero G +returns zero; nonzero G fails. Empty, inactive and zero-G/zero-H slots must be +zero for every rule. L1 is rejected by the default rule. A bounded rule can use +`context.xp.clip(NewtonLeafRule().values(G, H, config=config, context=context), +-c, c)` with a validated positive bound. Clipping changes leaf outputs and the +next round's gradients, while keeping the split criterion unchanged. + +CUDA reduction and rule arithmetic stay on device; scalar validation checks +synchronize. Named download-wrapper checks are not a complete profiler trace. +The assembled level-wise builder is described below. + +## Level-wise builder + +Select `LevelWiseBuilder(leaf_rule=..., memory_budget_bytes=...)` explicitly to +compose the batch primitives. It supports numeric, nonmissing features, L2, +full row/feature sampling and depth 0–8. Both children must have positive +curvature to split. Unsupported metadata/parameters and insufficient histogram +budgets fail before growth. The existing CPU default builder remains available. + +```python +import numpy as np +from openboost.experimental import ( + Booster, DistributionObjectiveAdapter, LevelWiseBuilder, TrainerConfig, +) + +rng = np.random.default_rng(7) +X = rng.normal(size=(64, 3)).astype(np.float32) +y = (0.5 * X[:, 0] + 0.3 * rng.normal(size=64)).astype(np.float32) +model = Booster( + objective=DistributionObjectiveAdapter("normal", natural=True), + tree_builder=LevelWiseBuilder(), + config=TrainerConfig(n_trees=2, max_depth=2, random_state=7), +).fit(X, y) +raw = model.predict_raw(X) +``` + +Direct `LevelWiseBuilder.build` also accepts a BinnedArray with contiguous CuPy +data and matching CUDA ExecutionContext. Metadata stays on host. This entry +currently requires the default CUDA stream. It returns a standard host +TreeStructure and an owned CuPy training prediction in BuiltTree. Only five +compact tree arrays are downloaded after growth; sample statistics, routed IDs +and histograms stay on device. Fixed slots and previous histogram release keep +one histogram batch live between levels; the budget excludes input arrays, +prediction caches, compact tree arrays and transient validation masks. + +The device cache survives release of input views. Host tree arrays support +CPU prediction and existing persistence. Fixed-slot growth applies splits with +fixed-shape selections and derives the next frontier from parent slots, retaining +unsplit leaves. Input validation and independent prediction-cache checks remain. Direct GPU builder composition is +validated separately from strict GPU `Booster.fit` integration. +The default numeric CPU builder is not replaced: this opt-in builder has a +narrower feature boundary and no established end-to-end performance advantage. + +## Independent extension packages + +The repository's `examples/extensions/` contains two separately buildable CPU/CUDA +packages. `normal_fisher` implements weighted Gaussian NLL gradients, expected +Fisher curvature and a nonconstant per-channel schedule. `bounded_leaves` +implements a bounded Newton leaf rule through the public API. They compose in +`demo.py` without core changes or private imports. + +From a development checkout, verify the real installation boundary with: + +```sh +uv run --no-sync python examples/extensions/verify_wheels.py /tmp/openboost-extension-evidence +``` + +The verifier builds three wheels and installs them in a fresh environment outside +the repository, runs independent mathematical and training checks, executes the +public example, then uninstalls both plugins. A new interpreter checks exact CPU +predictions for six saved models without the training packages. The example's +CPU dependency pins avoid the failed Intel macOS source build encountered with +Numba 0.63.1 / llvmlite 0.46.0; see its README for the tested versions and setup. + +This verifies a CPU extension installation boundary. These repository-authored +examples do not establish external adoption. GPU installation is checked separately +with the foundation `extensions` suite described below. + +## Strict CUDA fit boundary + +Set `device="cuda"` with a CUDA-capable objective. When no builder is specified, +CUDA selects `LevelWiseBuilder`; an explicit builder always takes precedence. +CPU retains `CPUHistogramBuilder`. Host numeric features/targets/weights enter +fit; binning and initialization run on CPU, then binned values, targets and +weights upload once. Raw state and per-round arithmetic use CuPy. Standard host +tree arrays finalize each tree; `predict_raw` remains CPU inference and saved +models load without training plugins. + +Strict CUDA rejects missing/categorical data, L1, sampling, eval sets, callbacks, +early stopping, unsupported leaf rules and non-default streams before training. +The first version supports numeric nonmissing L2/full-sampling trees. Invalid +plugin results, modified borrowed inputs or a cached prediction inconsistent with +the returned tree fail rather than retrying on CPU. CuPy lacks NumPy read-only +views, so the session copies borrowed inputs on-device and checks for mutation. +It independently traverses compact trees on-device to validate prediction caches. +These copies, compact uploads and scalar synchronization have a performance cost. + +`fit_report_` separates CPU binning/initialization, actual objective/tree/update +execution, fallback reason and transfer scope. Named transfer-wrapper tests do +not prove whole-process transfer absence. Profiler availability and real-device +results are recorded in the foundation evidence; no speedup is claimed here. + + +The independent examples at version 0.2.0 support explicit CUDA context arithmetic. +Use `python demo.py --device cuda` after installing both extension wheels and CUDA +dependencies. CPU initialization and host input requirements remain unchanged. +The foundation `extensions` suite installs all three wheels in an isolated T4 +container, checks GPU objective mathematics and composed training, then uninstalls +both training plugins before CPU inference in a new interpreter. See +`examples/extensions/README.md` for build and Modal commands. This establishes +an installation/conformance boundary, not third-party adoption or performance. diff --git a/docs/user-guide/formulaboost.md b/docs/user-guide/formulaboost.md index 916e616..d0a34ad 100644 --- a/docs/user-guide/formulaboost.md +++ b/docs/user-guide/formulaboost.md @@ -95,27 +95,14 @@ model.fit( ## What to evaluate -On the sales-curve benchmark (200K rows, train `x ∈ [0.25, 2.5]`, extrap -`x ∈ [3, 5]`): - -| | test RMSE | extrap RMSE | corr `b(z)` | -|---|--:|--:|--:| -| FormulaBoost `full` | 0.139 | **0.183** | **0.877** | -| FormulaBoost `diag` | 0.130 | 0.181 | 0.730 | -| black-box GBDT | 0.127 | 3.872 | n/a | -| XGBoost custom (diag Hess) | 0.129 | 0.203 | 0.599 | -| global `(a, b)` | 1.641 | 4.396 | n/a | - -Honest reading: - -- **Extrapolation** is the product claim (~21× vs black-box). The formula - constrains the shape in `x`; a GBDT that splits on `x` does not. -- **Parameter recovery** of `b(z)` is what `full` GGN buys. If you only - care about in-sample RMSE, `diag` is enough. -- `plain` is not a baseline you should ship (extrap RMSE ~3.9). - -Reproduce: `uv run modal run benchmarks/bench_formula.py`. Full notes: -[Benchmarks](../benchmarks.md). +Measure held-out error, extrapolation error and parameter recovery separately. +Compare full, diagonal and plain preconditioning against a global formula fit +and a tree baseline. A synthetic formula can test the mechanism but does not +establish real-world extrapolation performance. + +Run `uv run modal run benchmarks/bench_formula.py` and retain the raw results. +See [benchmark evidence requirements](../benchmarks.md); historical timing and +quality tables await committed provenance. ## Tips diff --git a/docs/user-guide/how-it-works.md b/docs/user-guide/how-it-works.md index 2d28461..a968b2f 100644 --- a/docs/user-guide/how-it-works.md +++ b/docs/user-guide/how-it-works.md @@ -51,23 +51,19 @@ the same job with histogram trees and a GPU path, and also fits varying-coefficient formulas and a censored Weibull whose shape depends on covariates, neither of which NGBoost can write down. -On the overlap (Normal NLL on UCI) quality is tied-or-better and GPU -training is much faster: [Benchmarks](../benchmarks.md). +Quality and performance comparisons require workload-specific evidence: +[Benchmarks](../benchmarks.md). ## Why the metric matters -Raw gradients of a multi-parameter likelihood are often badly scaled. -On the sales-curve formula, `precond="plain"` diverges (extrapolation RMSE -~3.9). Diagonal GGN is usable. Full GGN recovers the hard coefficient -(`b(z)` corr 0.877 vs 0.730 diag vs 0.599 for an XGBoost custom objective, -which cannot represent off-diagonals). +Raw gradients of a multi-parameter likelihood can have different scales. +FormulaBoost offers plain, diagonal and full GGN preconditioning; compare +convergence and parameter recovery on the intended formula. Full GGN retains +cross-parameter terms but does not guarantee better held-out error. -Same story for Weibull AFT: the observed Hessian's scale term explodes -when `λ` is wrong. The **expected** Fisher does not, and is what -`WeibullAFT` uses. - -`precond="full"` is the default for FormulaBoost. Do not turn it off -unless you are debugging. +WeibullAFT uses an expected-Fisher step rather than the observed Hessian. +These implementation choices require task-level evaluation; see the +[benchmark protocol](../benchmarks.md). ## Shared training API @@ -89,7 +85,7 @@ params = model.predict_params(X) # dict of per-row parameter arrays GPU: histogram trees run on CUDA when `openboost[cuda]` is installed. NaturalBoost Normal/Poisson have device gradient kernels. FormulaBoost GGN -is currently host-side (still faster than XGBoost-diag at 200K on A100). +is currently host-side. ## Choosing a model diff --git a/docs/user-guide/models/gradient-boosting.md b/docs/user-guide/models/gradient-boosting.md index b8d5386..e75df05 100644 --- a/docs/user-guide/models/gradient-boosting.md +++ b/docs/user-guide/models/gradient-boosting.md @@ -47,6 +47,7 @@ predictions = model.predict(X_test) | `n_bins` | int | 254 | Number of histogram bins | | `growth` | str | `'levelwise'` | Tree growth strategy: `'levelwise'`, `'leafwise'`, or `'symmetric'` | | `max_leaves` | int/None | None | Max leaves per tree for `'leafwise'` growth (defaults to `2**max_depth`) | +| `batch_size` | int/None | None | Reserved; non-None values raise `NotImplementedError` | | `random_state` | int/None | None | Seed for reproducible training | ## Loss Functions @@ -95,6 +96,11 @@ training path. CUDA, distributed, and multi-GPU training raise `NotImplementedError` when weights are supplied, so weighted observations are never silently treated as unweighted. +High-level mini-batch and out-of-core fitting are not implemented. The +`batch_size` parameter fails fast when set; low-level memmap and mini-batch +histogram helpers are experimental building blocks rather than a `model.fit` +path. + ## Feature Importance ```python diff --git a/docs/user-guide/naturalboost/overview.md b/docs/user-guide/naturalboost/overview.md index 7e6a08a..afa0be7 100644 --- a/docs/user-guide/naturalboost/overview.md +++ b/docs/user-guide/naturalboost/overview.md @@ -91,22 +91,12 @@ prob_exceed = np.mean(samples > threshold, axis=0) # P(Y > 10) q90 = np.percentile(samples, 90, axis=0) ``` -## vs NGBoost +## Comparison with NGBoost -On GPU (Modal A100, 90K rows, heteroscedastic Normal) NaturalBoost fits in -2.21s against NGBoost's 2716s, with NLL tied (2.108 vs 2.102). NGBoost has no -GPU implementation. This is one configuration from an early benchmark run, not -a settled result. - -On the NGBoost-paper UCI suite (20 paired splits, same budget) OpenBoost is -tied-or-better on every dataset that completed; significant NLL wins on -kin8nm, protein, and california; no significant loss. - -On CPU the two libraries are ~parity (0.8–1.3×). The speed claim is the -GPU tree path, not a faster CPU NGBoost clone. - -Full tables, caveats, and reproduce commands: -[Benchmarks](../../benchmarks.md). +Compare held-out NLL, CRPS and calibration at matched training budgets, then +measure end-to-end fit/prediction on explicitly recorded hardware. A GPU/CPU +comparison must state the different resources and include transfer/compilation +costs. See [available evidence and reproduction](../../benchmarks.md). ## Best Practices @@ -129,3 +119,23 @@ model = ob.NaturalBoostNormal( - [Weibull AFT](../survival.md): censored survival - [Custom distributions](custom-distributions.md) - [Benchmarks](../../benchmarks.md) + +### Execution and reproducibility boundary + +`NaturalBoost` and `DistributionalGBDT` accept `random_state` for CPU row and +column sampling. Repeating a fit with the same integer seed uses the same +sampling stream without changing NumPy's global RNG. `FormulaBoost` and +`WeibullAFT` share this trainer behavior. With `random_state=None`, each fit +creates its own unseeded generator. Legacy trainers have separate RNG paths. + +The unified CUDA trainer currently requires `subsample=1` and +`colsample_bytree=1`; other sampling ratios fail before binning or updates. +Only exact built-in Normal and Poisson distributions use device objective +kernels. Custom distributions, subclasses and exposure offsets use host +objective math with a visible warning. A native-tree fallback also warns; +these mixed execution paths must be distinguished in benchmark provenance. +Kernel compilation or execution errors propagate as failed fits. + +Sample weights multiply both gradient and Hessian. Weighted fits disable the +unit-Hessian bandwidth hint, including uniform weights. Initialization still +uses the existing unweighted `distribution.init_params(y)` estimate. diff --git a/docs/user-guide/survival.md b/docs/user-guide/survival.md index bf7f1b5..d7e2848 100644 --- a/docs/user-guide/survival.md +++ b/docs/user-guide/survival.md @@ -72,24 +72,14 @@ censored NLL. | `predict_survival(X, t)` | `S(t \| z)` for a scalar or per-row `t` | | `nll(X, y, event=)` | mean censored negative log-likelihood | -## vs XGBoost `survival:aft` +## Evaluation -Synthetic DGP where **both** `λ(z)` and `k(z)` vary, ~35% right-censoring, -200K rows, 300 rounds: +Compare censored NLL, ranking, interval coverage and shape recovery separately. +A synthetic process with covariate-dependent scale and shape tests whether +both parameter surfaces can be recovered; it does not establish domain value. -| | C-index | NLL | 80% coverage | shape corr | fit | -|---|--:|--:|--:|--:|--:| -| OpenBoost `WeibullAFT` | **0.680** | **0.761** | 0.803 | **0.997** | 5.4s | -| XGBoost `survival:aft` (`extreme`) | 0.672 | 0.830 | 0.852 | n/a (global `k=1.34`) | 11.7s | -| global constant | 0.500 | 0.896 | 0.810 | n/a | n/a | - -C-index is close, since ranking mostly follows the scale. The NLL gap and the -shape correlation are the capability: OpenBoost recovers `k(z)`, XGBoost -cannot represent it. Coverage of the 80% interval is nearer the nominal -0.80 (XGBoost over-covers). - -Reproduce: `uv run modal run benchmarks/bench_survival.py`. Notes: -[Benchmarks](../benchmarks.md). +Run `uv run modal run benchmarks/bench_survival.py` and retain the raw results. +See [benchmark evidence requirements](../benchmarks.md). ## Tips diff --git a/docs/user-guide/training/large-scale.md b/docs/user-guide/training/large-scale.md index 40fdab7..b87ddba 100644 --- a/docs/user-guide/training/large-scale.md +++ b/docs/user-guide/training/large-scale.md @@ -1,71 +1,69 @@ -# Large-Scale Training +# Scaling Training -Train on datasets that don't fit in memory or need faster training. +OpenBoost has a supported full-dataset CPU/CUDA path, a supported sampling +option, and several experimental scaling primitives. Keep those categories +separate when choosing a training path or reporting a benchmark. + +| Capability | Status | Important boundary | +|------------|--------|--------------------| +| Single-device full-data training | Supported | Dataset and histograms must fit in memory | +| GOSS sampling | Supported | Speed and quality are data-dependent | +| `fit_trees_batch` | Reference implementation | Shares bins; configurations fit sequentially | +| Mini-batch histogram helpers | Low-level primitive | Not integrated with `model.fit` | +| Memory-mapped binned arrays | Storage primitive | Not an out-of-core `model.fit` path | +| Distributed/multi-GPU | Experimental | No published parity or scaling artifact yet | ## GOSS Sampling -Gradient-based One-Side Sampling (from LightGBM) - train 3x faster with minimal accuracy loss. +Gradient-based One-Side Sampling keeps high-gradient observations and samples +from the remainder on every boosting round: ```python import openboost as ob model = ob.GradientBoosting( n_trees=100, - subsample_strategy='goss', - goss_top_rate=0.2, # Keep top 20% high-gradient samples - goss_other_rate=0.1, # Sample 10% of the rest + subsample_strategy="goss", + goss_top_rate=0.2, + goss_other_rate=0.1, + random_state=42, ) model.fit(X_train, y_train) ``` -### How GOSS Works - -1. Sort samples by gradient magnitude -2. Keep top `goss_top_rate` samples (most informative) -3. Randomly sample `goss_other_rate` from the rest -4. Weight the random samples to maintain unbiased gradients - -### GOSS Parameters - -| Parameter | Default | Description | -|-----------|---------|-------------| -| `goss_top_rate` | 0.2 | Fraction of high-gradient samples to keep | -| `goss_other_rate` | 0.1 | Fraction of remaining samples to keep | +With these rates, approximately 28% of observations participate in a round. +That arithmetic is not a speed or accuracy guarantee: compare GOSS with the +full-data path on fixed folds and seeds before using it. -**Result**: Train on ~28% of samples with similar accuracy. +## Single-GPU Scaling -## Memory-Mapped Arrays - -For datasets larger than RAM: +Select CUDA explicitly so an unavailable GPU cannot silently turn a benchmark +into a CPU run: ```python import openboost as ob -# Create memory-mapped binned array (saves to disk) -X_mmap = ob.create_memmap_binned('large_data.npy', X_large) - -# Load for training (no copy, uses disk) -X_mmap = ob.load_memmap_binned('large_data.npy', n_features, n_samples) - -# Train as normal -model = ob.GradientBoosting(n_trees=100) -model.fit(X_mmap, y_train) +ob.set_backend("cuda") +model = ob.GradientBoosting(n_trees=100, random_state=42) +model.fit(X_train, y_train) ``` -## Mini-Batch Training +Report the OpenBoost commit, environment, GPU model, driver/CUDA versions, +warm-up policy, seeds, fit time, prediction time, peak memory, and CPU/CUDA +prediction parity. Use the scale-extension protocol in +`benchmarks/scoringbench/` for probabilistic benchmark work. -Accumulate histograms in batches: +## Mini-Batch and Memory-Mapped Primitives -```python -from openboost import MiniBatchIterator, accumulate_histograms_minibatch +`MiniBatchIterator`, `accumulate_histograms_minibatch`, +`create_memmap_binned`, and `load_memmap_binned` are low-level building blocks. +The memmap layout is feature-major `(n_features, n_samples)`; it is not a raw +sample-major matrix accepted by high-level `model.fit`. -# Process 100k samples at a time -hist_grad, hist_hess = accumulate_histograms_minibatch( - X_mmap, grad, hess, - batch_size=100_000, - n_features=n_features, -) -``` +The `batch_size` model parameter is reserved. Passing a non-`None` value raises +`NotImplementedError` rather than pretending to perform mini-batch training. +Do not claim datasets larger than memory are supported end to end until a +training loop integrates these primitives and has correctness tests. ## Train Many Configurations @@ -90,65 +88,32 @@ trees_by_config = ob.fit_trees_batch( ) ``` -The current implementation is a correctness reference: it shares binned input -data but fits configurations sequentially. GPU kernel fusion is planned without -changing this API's results. +The current implementation shares the binned input but fits configurations +sequentially. Treat it as a correctness reference, not fused GPU training. -## Multi-GPU Training +## Experimental Multi-GPU Path -Distribute training across multiple GPUs: +The Ray multi-GPU path is available for development experiments: ```python import openboost as ob -# Automatic multi-GPU with Ray -model = ob.GradientBoosting(n_trees=100, n_gpus=4) -model.fit(X, y) - -# Or specify exact devices -model = ob.GradientBoosting(n_trees=100, devices=[0, 2]) -model.fit(X, y) -``` - -### Requirements - -```bash -pip install "openboost[distributed]" # Installs Ray +model = ob.GradientBoosting(n_trees=100, devices=[0, 1]) +model.fit(X_train, y_train) ``` -## Scaling Guidelines +It does not support `sample_weight`, and the repository does not yet contain a +validated two-/four-GPU parity and scaling artifact. Do not use it for release +claims until exact single-device parity, repeated timings, peak memory, and +failure cases are published on real multi-GPU hardware. -| Dataset Size | Recommendation | -|--------------|----------------| -| <100K samples | Standard training | -| 100K-1M | GOSS sampling | -| 1M-10M | GOSS + memory-mapped | -| >10M | Multi-GPU + GOSS | +## Evidence Gate -## Example: Large Dataset +A scaling claim is ready only when the checked-in artifact records: -```python -import numpy as np -import openboost as ob - -# Simulate large dataset (10M samples) -n_samples = 10_000_000 -n_features = 100 - -# Create memory-mapped data -X_mmap = ob.create_memmap_binned( - 'large_X.npy', - np.random.randn(n_samples, n_features).astype(np.float32) -) - -y = np.random.randn(n_samples).astype(np.float32) - -# Train with GOSS -model = ob.GradientBoosting( - n_trees=100, - subsample_strategy='goss', - goss_top_rate=0.1, - goss_other_rate=0.05, -) -model.fit(X_mmap, y) -``` +1. a frozen OpenBoost commit and dependency lock; +2. real datasets plus at least one controlled synthetic scaling curve; +3. CPU/CUDA prediction parity and task-quality metrics; +4. repeated fit/predict timings after an explicit warm-up policy; +5. hardware, drivers, thread counts, peak memory, seeds, and failures; +6. comparisons against maintained baselines under the same protocol. diff --git a/docs/v1/aft.md b/docs/v1/aft.md new file mode 100644 index 0000000..6f693f0 --- /dev/null +++ b/docs/v1/aft.md @@ -0,0 +1,56 @@ +# Event/right-censored log-normal AFT + +AFT models log time as raw + offset + sigma*Z with standard Normal Z and fixed +positive sigma. Bind [lower, upper] targets using target_kind="event_right": +exact events have equal positive bounds; right-censored observations have a +finite positive lower bound and upper=+infinity. Left/interval censoring and +truncation are unsupported and rejected. Ordinary numeric targets remain finite. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import aft +from openboost.survival import AFTModel + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[1, 1], [2, np.inf], [4, 4], [8, np.inf]], x.row_ids, + target_kind="event_right") +valid = Problem(v, [[2, 2], [6, np.inf]], v.row_ids, target_kind="event_right") +sigma = 0.7 +fit = aft(train, valid, context=RunContext("aft-demo", 7), sigma=sigma, rounds=3) +model = AFTModel(fit.state.best_model, sigma) +output = model.predict(v, times=[1, 3, 10], probabilities=[0.1, 0.5, 0.9]) +assert output["survival"].shape == (2, 3) +assert np.all(np.diff(output["survival"], axis=1) <= 0) +``` + +LogNormalAFT.geometry returns weighted censored negative log likelihood, +unweighted gradients and diagonal curvature. Exact-event density includes its +time Jacobian and Normal constant; right censoring uses negative log survival, +not event density. Original weights enter the shared Newton fields once. +Offsets enter geometry once and remain outside accepted raw caches. + +Normal upper tails use erfc centrally and Gauss–Laguerre quadrature above z=8, +computing the small curvature term without subtracting nearly equal values. +Tests compare this with an independent continued fraction, including extreme +positive standardized times. Unrepresentable geometry fails explicitly. + +Initialization is the weighted average of log lower bounds minus offset. +It is a finite starting location, not a censoring-adjusted intercept optimum; +even an all-censored sample can initialize. Fixed/backtracking steps and +validation selection use censored likelihood. Censored lower bounds are not +treated as observed deaths for evaluation. + +AFTModel stores the raw model and declared sigma in openboost-aft-lognormal-v1. +Use the same sigma used for fitting. Loading validates both and needs no +training objective or observations. predict requires a nonempty positive times +grid and accepts probabilities strictly between zero and one. Outputs are median, +mean, survival [N,times] and quantile [N,probabilities]. Mean is exp(F+sigma²/2), +median exp(F); supply inference offsets explicitly. + +Target kind participates in problem identity, and valid +infinity survives +binding. Tests cover bounds rejection, three-round geometry/tree parity, +finite differences, censoring effects, monotone outputs and fresh-process +mixed/missing/unseen inference. Real A10 NLL/IPCW comparisons, calibration, +learned scale, other censoring forms and CUDA remain open. diff --git a/docs/v1/binary.md b/docs/v1/binary.md new file mode 100644 index 0000000..a2c753c --- /dev/null +++ b/docs/v1/binary.md @@ -0,0 +1,52 @@ +# Binary classification with explicit class order + +Fit ClassSchema on training labels only, then encode both training and validation +labels using it. Schemas sort homogeneous string/integer labels, reject missing +labels and require at least two classes. Validation may contain one known class; +unknown labels are rejected. Binary training requires both observed codes. + +```python +from openboost import ClassSchema, MixedData, Problem, RunContext +from openboost.recipes import binary + +schema = ClassSchema.fit(["no", "yes", "no", "yes"]) +x = MixedData([[0, "a"], [1, "b"], [2, "a"], [3, None]], [1, 2, 3, 4], + ("x", "group"), ("numeric", "categorical")) +v = MixedData([[0.5, "a"], [2.5, "unknown"]], [10, 11], x.feature_names, x.feature_kinds) +train = Problem(x, schema.encode(["no", "yes", "no", "yes"]), x.row_ids, classes=schema) +valid = Problem(v, schema.encode(["no", "yes"]), v.row_ids, classes=schema) +fit = binary(train, valid, context=RunContext("binary-demo", 7), rounds=3) +model = fit.state.best_model +probabilities = model.predict_proba(v) # columns match model.classes.values +labels = model.predict_label(v) +assert probabilities.shape == (2, 2) +assert all(label in schema.values for label in labels) +``` + +Raw state remains `[N, 1]` logits. `Binary.geometry` exposes weighted mean logistic +loss and unweighted gradient/curvature. Signed-margin logaddexp loss and separate +sigmoid tails preserve small gradients for confident correct predictions. There +is no hidden curvature floor. The shared Newton adapter applies original weights +once; categorical/numeric trees and fixed/backtracking transactions are unchanged. + +Initialization is the logit of clipped training weighted prevalence (clip=1e-6), +minus weighted mean offset. With heterogeneous offsets this is a deterministic +initializer, not an optimized intercept. Clip must be positive, less than 0.5, +and represent an upper probability below 1. Offsets affect objective geometry +once and remain outside accepted raw caches. + +`model.predict` returns raw logits. `predict_proba` returns columns for the two +persisted class labels; `predict_label` decodes argmax, with exact ties selecting +the first sorted label. Supply inference offsets explicitly to these methods. +Probability inference uses an inference-only transform and needs no training +objective/data. Outputs are probabilities, not a calibration guarantee. + +Class order is part of Problem/model identity and follows every accepted/best +snapshot. Mismatched train/validation schemas fail. The versioned ensemble format +now includes class labels; malformed/unsupported schemas and earlier formats fail +loading. Regression recipes reject classification-tagged problems rather than +silently treating class codes as numeric regression targets. + +This slice verifies binary geometry and complete mixed-feature inference. The binary +recipe accepts exactly two classes. [Multiclass](multiclass.md) provides a separate +joint vector-tree recipe. Broader classification quality evaluation remains open. No XGBoost/LightGBM/CatBoost parity or CUDA claim. diff --git a/docs/v1/categorical.md b/docs/v1/categorical.md new file mode 100644 index 0000000..46bfd81 --- /dev/null +++ b/docs/v1/categorical.md @@ -0,0 +1,57 @@ +# Categorical features through shared operations + +`MixedData` declares ordered feature kinds. Numeric columns accept finite values +or missing values; categorical columns accept homogeneous strings or integers. +Booleans, fractional category IDs and mixed token types within one column are +rejected. None/NaN represent missingness. NumericData remains the numeric-only +input record. MixedData owns immutable tuples; `.values` exports a detached object +array, so editing that export cannot alter the original data or its identity. + +```python +import numpy as np +from openboost import MixedData, Problem, RunContext +from openboost.binning import Binning +from openboost.recipes import squared + +x = MixedData([[0, "a"], [1, "b"], [2, "a"], [3, None]], [1, 2, 3, 4], + ("value", "group"), ("numeric", "categorical")) +p = Problem(x, [[-2], [3], [-1], [2]], x.row_ids) +fit = squared(p, p, context=RunContext("categories", 7), rounds=2, bins=4) +unseen = MixedData([[4, "new"], [5, None]], [10, 11], x.feature_names, x.feature_kinds) +fitted = Binning.fit(x, bins=4) +assert fitted.categories[1] == ("a", "b") +assert fitted.transform(unseen).missing[1].all() +assert np.isfinite(fit.state.model.predict(unseen)).all() +``` + +The example reuses training/validation data for mechanics only. Real evaluation +requires the prescribed separate partitions. + +Binning fits sorted unique dictionaries on training input only. Unseen tokens +follow missing routes; dictionaries do not expand during inference. An all-missing +training column has an empty dictionary and generates no split candidates. A +single observed category can split against missing rows. Binning identity includes +cuts, dictionary token types/order and feature kinds. Reusing codes from another +transformer fails identity checks. + +Histograms retain physical counts and additive fields. Each categorical candidate +selects one dictionary value versus all other observed values, with both missing +directions considered. `Candidate.kind` exposes equality versus numeric threshold +semantics to callbacks. Depthwise, best-first and symmetric policies share those +candidates and original-row routing. This is not category subset search, ordered +target statistics or full CatBoost/LightGBM categorical parity. + +The public names `Binning` and `Tree` replace NumericBinning and NumericTree; +there are no compatibility aliases. `openboost-tree-v3` persists typed dictionaries, +numeric cuts, explicit topology and missing routes. Old tree formats fail loading. +Inference validates feature kinds as well as names. Unknown tokens are allowed; +invalid dictionaries, out-of-range conditions and corrupt topology fail. Raw +ensemble artifacts embed this tree record, so no category fitting is needed on load. + +CPU squared, Normal and Formula recipes accept mixed features through the same +preparation and learner path. This slice directly verifies complete squared +composition and all three mixed-feature growth policies; it does not establish +real classification or distributional quality parity. Specialized leaves, +CUDA and full evaluation remain required work. +[Multiclass and vector leaves](multiclass.md) now share this mixed-feature path. +[Binary classification](binary.md) now includes a verified mixed-feature recipe. diff --git a/docs/v1/cpu-state.md b/docs/v1/cpu-state.md new file mode 100644 index 0000000..d34d717 --- /dev/null +++ b/docs/v1/cpu-state.md @@ -0,0 +1,83 @@ +# Initial CPU state components + +B03 provides public numeric inputs, run identity, immutable state transitions and +ensemble artifacts. The [squared recipe](squared.md) now composes these components. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.runtime import initialize, propose, resolve + +x = NumericData([[1.0], [2.0]], [10, 20], ("feature",)) +problem = Problem(x, [[5.0], [7.0]], [10, 20], + weight=[1.0, 3.0], offset=[[2.0], [4.0]]) + +def score(problem, raw): + error = problem.with_offset(raw) - problem.target + return float(np.dot(problem.weight, error[:, 0] ** 2) / problem.weight.sum()) + +state = initialize(RunContext("demo", seed=7), problem, problem, [0.0], score=score) +trial = propose(state, [6.0], coefficient=0.5) +assert resolve(state, trial, accept=False, score=score) is state +accepted = resolve(state, trial, accept=True, score=score) +np.testing.assert_array_equal(accepted.train_raw, [[3.0], [3.0]]) +np.testing.assert_array_equal( + accepted.model.predict(x, offset=problem.offset), [[5.0], [7.0]] +) +assert accepted.best_score == 0.0 +``` + +The example deliberately uses the same problem for train and validation to expose +state arithmetic; real evaluation must use the prescribed separate partitions. + +`NumericData` owns float64 CPU features and unique integer row IDs. Feature names +are ordered. NaN represents numeric missingness; infinity is rejected. No binning +is fitted by this record; use Binning separately. Use [MixedData](categorical.md) +for explicit numeric/categorical columns. Owned array values cannot be made writable; +callers must not alter array metadata. Identity includes feature content/order, +row IDs and schema, and is computed once on construction. + +`Problem` requires targets shaped `[N, T]`, offsets `[N, raw_width]` and row +weights `[N]`. `raw_width` defaults to T; set it explicitly when parameters differ +from observations, such as scalar Normal targets with two raw parameters. +One explicit row-ID vector declares the order of all role arrays; binding rejects +a mismatch instead of sorting. The caller is responsible for aligning each role +before binding. Ordinary targets are finite numeric values. [AFT](aft.md) declares +target_kind="event_right" for validated [lower,upper] bounds, retaining only +valid upper +infinity and defaulting raw_width to one. [ClassSchema](binary.md) +binds typed training-label order to encoded classification targets. Named `structure` +arrays are separately owned `[N, S]` roles in the declared row order; they affect +problem identity but are never automatically appended to features. Recipes must +consume or reject them; squared and Normal reject supplied structure. Unknown arguments +are rejected. Original weights are retained, never automatically applied here. + +`RunContext.rng(round_index, component, purpose)` returns a new deterministic +stream for that logical key. Reusing the key reproduces the stream; different +run IDs distinguish streams. It neither mutates global NumPy RNG nor advances a +shared run cursor. CUDA devices are explicitly rejected in B03. + +Accepted raw caches contain the base and accepted term sum, without offsets. +Algorithm code owns `score(problem, raw)`, including exactly-once weights and +offsets. Smaller validation scores are better. `resolve(..., accept=True)` need +not improve validation; it commits the term while preserving an earlier best +model when appropriate. A rejected proposal returns the identical state. Stale +parents, other runs and divergent parent histories are rejected. Nonfinite scoring +fails before a new state is returned. Vector terms commit jointly. + +`Model.save(path)` / `Model.load(path)` use the explicit +`openboost-ensemble-v2` JSON format. This replaces the earlier constant-only format +and rejects earlier ensemble versions. Model replaces ConstantModel without a +compatibility shim. Optional class order is bound to state and persisted. It records feature +names, vector base, constant terms and vector tree terms with explicit `[L, K]` +output matrices and one coefficient each. `propose_terms` submits multiple terms +as one atomic update. Offsets are supplied at inference and +are never embedded as training-row offsets. Loading needs no training objective. +This is an inference artifact, not a training-resume checkpoint; it does not store +run RNG, best/stop history or input datasets. Unknown versions/fields, duplicate +fields, nonfinite payloads and inconsistent output widths fail. + +B04 adds numeric preparation and shared tree operations. The squared recipe is +available alongside joint Normal updates with ordinary/Fisher directions. Initial B06 now exercises Formula and +heterogeneous sequential runs; interfaces remain provisional. Model construction +uses a conservative absolute-value envelope to reject possible prediction +overflow, which can reject extremely large terms even when they would cancel. diff --git a/docs/v1/extensions.md b/docs/v1/extensions.md new file mode 100644 index 0000000..ff590a3 --- /dev/null +++ b/docs/v1/extensions.md @@ -0,0 +1,21 @@ +# Public development extensions + +The repository's `examples/v1_extensions/` contains four separately installable CPU +packages using only public OpenBoost interfaces. `ob-expectile` implements +weighted expectile geometry, initialization and a two-round Newton recipe. `ob-cohort-splits` supplies +independent cohort information and custom split feasibility. `ob-penalized-leaves` +replaces leaf solving with its own weighted pinball/quadratic optimizer. +`ob-ordered-updates` provides ordered Normal/Formula parameter updates through +public transactions, with fresh geometry after every accepted parameter and +outer-round stopping. Built-in recipes remain joint. Its custom result is accepted +by run_many through the [shared structural result contract](results.md). + +These reuse histogram/routing, recipe state and model artifacts. Three-round +checks compare independent mathematical oracles and demonstrate that changed +leaves affect subsequent updates. A separate wheel verifier installs all four +packages outside the source checkout, removes them after training and verifies +fresh-process core inference on their saved models. + +See the example directory's README and verifier for reproduction. These are +repository-authored development probes. They establish neither independent +authoring advantage nor adoption, full E2/E6 acceptance or CUDA execution. diff --git a/docs/v1/formula-runs.md b/docs/v1/formula-runs.md new file mode 100644 index 0000000..d638871 --- /dev/null +++ b/docs/v1/formula-runs.md @@ -0,0 +1,68 @@ +# Formula and heterogeneous sequential runs + +The saturation Formula models `a * (1 - exp(-b*x))`, where a and b are softplus +transforms of two raw parameters. Structural x is a positive `[N, 1]` role supplied +separately from feature columns. Trees predict parameters from features; the +formula combines those parameters with structural x. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.objectives import Formula +from openboost.recipes import formula, squared +from openboost.runs import RunSpec, run_many + +x = NumericData([[0], [1], [2], [3]], [10, 11, 12, 13], ("feature",)) +y = [[0.5], [1], [2], [3]] +structured = Problem(x, y, x.row_ids, raw_width=2, + structure={"x": [[0.2], [0.5], [1], [2]]}) +scalar = Problem(x, y, x.row_ids) +specs = [RunSpec(RunContext("formula", 7), structured, structured, formula, + {"rounds": 2, "bins": 4}), + RunSpec(RunContext("squared", 7), scalar, scalar, squared, + {"rounds": 1, "bins": 4})] +results = run_many(specs) +assert all(item.error_type is None for item in results) +raw = results[0].result.state.model.predict(x) +prediction = Formula.predict(raw, structured.structure["x"]) +assert prediction.shape == (4, 1) and np.isfinite(prediction).all() +``` + +The example shares train/validation inputs only to demonstrate composition. Use +separate prescribed partitions for real evaluation. + +`Formula.geometry` returns weighted half-square loss, unweighted gradient and +per-row GGN `J.T @ J`. It is rank one; `full_direction(..., damping=...)` uses a +symmetric positive-definite Cholesky solve and rejects numerically singular +systems. The recipe defaults to damping=0.1. There is no implicit pseudoinverse +or diagonal fallback. Learners fit each direction with the existing once-weighted +least-squares adapter, and mapped terms commit jointly through shared backtracking. +The initial base uses inverse-softplus of max(weighted target mean, 1e-6) and 1; +it is an initializer, not the coupled optimum. Offsets apply to raw parameters +before geometry and output. A fixed two-parameter dense GGN is used here; this +is not a memory guarantee for arbitrary high-dimensional models. + +`Model.save/load` persists the raw ensemble. At inference, provide structural x +again to `Formula.predict`, and apply any parameter offsets through Model.predict. +No training-row structure or formula tag is embedded in the raw model. This is +one explicit formula probe, not a general symbolic-expression engine. + +`RunSpec` declares context, train/validation problems, recipe and copied immutable +scalar options. Problems can share NumericData without sharing accepted state. +`run_many` validates unique IDs before running, returns every outcome in requested +order, and records recipe exceptions while continuing other jobs. Result context +and problem identities must match the spec. Completed results follow the +[shared result contract](results.md); external recipes retain their own result +types and per-round payloads. KeyboardInterrupt/SystemExit propagate. +An expected injected failure tests isolation; actual required failures still fail +evaluation. Reusing a run ID in a separate invocation replays its logical identity. + +Only sequential execution is supported. This provides independent recipe results, +per-run round budgets, best snapshots and errors, not process isolation, fused +training or a resource scheduler. [Validation patience](stopping.md) is a public +operation also used by the built-in recipes. Arbitrary recipe code +must respect the immutable input contract. M=1/2/8 comparisons verify deterministic +same-ID independent and reordered execution; they establish no speed benefit. + +[Shared preparation](preparation.md) now supports explicit training-code reuse +across independent runs. diff --git a/docs/v1/frequency-severity.md b/docs/v1/frequency-severity.md new file mode 100644 index 0000000..7c95506 --- /dev/null +++ b/docs/v1/frequency-severity.md @@ -0,0 +1,55 @@ +# Positive-payment frequency and severity + +A two-model composition predicts positive-payment count rate times mean positive +payment. Counts must refer to the same eligible payment records as severity. +Raw claim counts that include zero payments are not interchangeable. + +```python +import numpy as np +from openboost import NumericData, RunContext +from openboost.composition import paid_loss_problems, FrequencySeverity +from openboost.recipes import poisson, gamma + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +ftrain, strain = paid_loss_problems(x, [0, 2, 1, 3], [0, 6, 2, 15], [0.5, 1, 2, 1]) +fvalid, svalid = paid_loss_problems(v, [1, 2], [3, 8], [1, 0.5]) +frequency = poisson(ftrain, fvalid, context=RunContext("frequency", 7), rounds=3) +severity = gamma(strain, svalid, context=RunContext("severity", 7), rounds=3) +model = FrequencySeverity(frequency.state.best_model, severity.state.best_model) +output = model.predict(v, v, [1, 0.5]) +np.testing.assert_allclose(output["period_mean"], output["annualized_mean"] * [1, 0.5]) +``` + +paid_loss_problems binds declared policy aggregates to two problems: +Poisson paid counts with explicit exposure and business weights, and Gamma +positive policy-average payments with weights equal to business weight times +paid count. Zero-count policies remain in frequency but not severity. Counts +must be integers; positive counts require positive totals, and zero counts require +zero totals. At least one positive-weight paid policy is required for severity. +The helper uses the same predictors within each policy; it does not retain +claim-specific features or reconstruct raw joins. + +The caller must filter eligible payments, aggregate by policy and handle +contradictory source records before constructing these inputs. The helper cannot +prove that supplied counts correspond to the supplied totals. Keep policy +entities in separate evaluation partitions. + +FrequencySeverity accepts declared scalar regression models. Its inference +inputs may have different feature schemas but must carry identical policy row +IDs in the same order. Separate frequency_offset/severity_offset arguments are +applied to the respective raw models. Exposure scales paid count and period +loss once. Outputs name paid_count_rate, paid_count_mean, severity_mean, +annualized_mean and period_mean. Means are not calibrated aggregate distributions. + +save/load use openboost-frequency-severity-v1, embedding both validated raw +models with fixed output roles. Corrupt/duplicate fields and invalid model +widths fail. The artifact is self-contained for inference; new exposure, +features and any offsets remain caller inputs. Model identities preserve which +dependency has each role, but cannot certify its training provenance. + +The example selects each component by its own validation objective. Joint +selection by aggregate-loss quality requires an explicit workflow evaluation. +Tests verify three-round component training, products, units, row reordering, +aggregate rejection and fresh-process mixed-feature persistence. Real A9 +quality/joins, joint selection, AFT and CUDA remain open. diff --git a/docs/v1/gamma.md b/docs/v1/gamma.md new file mode 100644 index 0000000..8c8e347 --- /dev/null +++ b/docs/v1/gamma.md @@ -0,0 +1,47 @@ +# Gamma positive-target means + +The CPU Gamma recipe accepts strictly positive scalar targets and predicts +exp(raw + offset). It fits a mean with the unit-dispersion objective +target/mean + log(mean); dispersion is not estimated. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import gamma +from openboost.outputs import positive_mean + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[0.5], [2], [3], [8]], x.row_ids, weight=[1, 2, 1, 3]) +valid = Problem(v, [[1], [4]], v.row_ids) +fit = gamma(train, valid, context=RunContext("gamma-demo", 7), rounds=3) +mean = positive_mean(fit.state.best_model.predict(v)) +assert np.isfinite(mean).all() and (mean > 0).all() +``` + +Gamma.geometry exposes weighted mean loss and unweighted derivatives with +respect to log mean: gradient=1-target/mean, curvature=target/mean. +The shared Newton adapter applies original weights once. Initialization is +log(weighted_mean(target*exp(-offset))), evaluated with log sums. Offsets enter +geometry once and remain outside raw caches. + +Fixed/backtracking steps and validation selection use this Gamma objective. +For fixed targets it orders predictions like Gamma deviance, but the returned +loss is not a fitted-dispersion likelihood or a reported deviance statistic. +Zero/negative targets, unknown structure roles and unrepresentable geometry +are rejected. Exposure has no automatic meaning in this recipe. + +For severity applications, define eligible positive claims and their selection +rules outside training. Claim-level targets and policy-average targets have +different weight meanings. When predictors/offsets are identical within a policy, +a positive claim average weighted by claim count reproduces the summed +claim-level gradients and curvature. Unit policy weights generally do not. + +Generic artifacts retain raw base/trees. Call positive_mean after model.predict, +supplying inference offsets explicitly. This transform produces one mean per row, +without training targets or a fitted distribution. Fresh-process tests cover +numeric/categorical/missing/unseen input composition. + +Independent tests verify derivatives, offset-aware base, three rounds, weight +semantics, rejected updates and persistence. Real A8 quality/selection evaluation, +dispersion/calibration, Tweedie/composition/A9, AFT/A10 and CUDA remain open. diff --git a/docs/v1/index.md b/docs/v1/index.md new file mode 100644 index 0000000..3d67e4f --- /dev/null +++ b/docs/v1/index.md @@ -0,0 +1,59 @@ +# OpenBoost v1 + +OpenBoost is a programmable boosting foundation for researchers and agents, under +construction. Initial public CPU ownership, run-state and mapped ensemble artifact +components are available; see [CPU state usage](cpu-state.md). + +[Numeric operations](numeric-ops.md) provide binning, histograms, candidate +selection, routing and scalar leaves. [Numeric tree policies](trees.md) compose these +operations and support numeric inference/persistence. The first complete +[squared-error recipe](squared.md) provides fixed/backtracking CPU boosting. +[Normal boosting](normal.md) uses the same state and scalar learners for joint +mean/log-scale updates. [Formula and sequential runs](formula-runs.md) add +full-metric structured updates and independent heterogeneous execution. CUDA +execution is not implemented yet. [Categorical support](categorical.md) now uses +explicit dictionaries and equality conditions in all three growth policies. +All R1–R9/C1–C7/A1–A13 remain required. Evaluation preparation continues alongside +the user-approved B03–B06 construction overlap. No complete quality, speed or +agent/adoption result is claimed for the new production foundation. + +The old production API was retired. Historical examples require revision +`50acfc6`; current APIs are not backward compatible. See the repository's +`v1-sprints/` for construction records and `planning/` for requirements and gates. + +[Binary classification](binary.md) adds typed class schemas, stable logistic +geometry and persisted probability/label output through the same foundation. +[Multiclass and vector leaves](multiclass.md) add joint softmax updates, separate +split/leaf statistics and arbitrary learner-to-model output mappings. + +[Query-local ranking](ranking.md) adds pairwise/lambda CPU geometry and +fixed-step recipes with validation NDCG selection. Real A4 evaluation remains open. + +[Quantile and penalized leaves](quantile.md) expose routed residuals/original +weights and compose all three CPU growth policies. Real A5 evaluation remains open. + +[Poisson counts and exposure](poisson.md) add a CPU count recipe with explicit +rate/count outputs. Real A7 evaluation remains open. + +[Gamma positive-target means](gamma.md) add weighted CPU mean regression. +Real A8 quality and distributional calibration remain unverified. + +[Tweedie nonnegative means](tweedie.md) support fixed-power CPU fitting and +explicit annualized-loss weight semantics. Real A9 evaluation remains open. + +[Frequency–severity composition](frequency-severity.md) binds matched paid-loss aggregates +and persists two-model inference with explicit output units. Real A9 evaluation remains open. + +[Log-normal AFT](aft.md) adds event/right-censored CPU training and +persisted scale-aware survival outputs. Real A10 evaluation remains open. + +[Multi-output squared regression](multioutput.md) supports independent/shared trees, +projected splits and persisted training-only target scaling. Real A6 evaluation remains open. + +[Shared training preparation](preparation.md) reuses fitted CPU binning/codes +across independent jobs, verified at M=1/8/32. +[Independent validation stopping](stopping.md) separates outer-round patience +from model acceptance and strict best-model selection across all CPU recipes. + +[Public development extensions](extensions.md) exercise installed cohort split +constraints and external penalized leaves, with core inference after plugin removal. diff --git a/docs/v1/multiclass.md b/docs/v1/multiclass.md new file mode 100644 index 0000000..129b46e --- /dev/null +++ b/docs/v1/multiclass.md @@ -0,0 +1,60 @@ +# Multiclass and vector leaves + +The CPU multiclass recipe grows one shared-topology vector tree per round. +All class directions use the same accepted raw snapshot and commit together. +ClassSchema preserves sorted class order through training and inference. + +```python +import numpy as np +from openboost import ClassSchema, MixedData, Problem, RunContext +from openboost.recipes import multiclass + +schema = ClassSchema.fit(["red", "green", "blue"]) +x = MixedData([[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "b"], [5, "c"]], + range(6), ("x", "group"), ("numeric", "categorical")) +v = MixedData([[0.5, "a"], [4.5, "unknown"]], [10, 11], + x.feature_names, x.feature_kinds) +train = Problem(x, schema.encode(["red", "green", "blue", "red", "green", "blue"]), + x.row_ids, raw_width=3, classes=schema) +valid = Problem(v, schema.encode(["red", "blue"]), v.row_ids, + raw_width=3, classes=schema) +fit = multiclass(train, valid, context=RunContext("multiclass-demo", 7), rounds=3) +model = fit.state.best_model +probabilities = model.predict_proba(v) +assert probabilities.shape == (2, 3) +np.testing.assert_allclose(probabilities.sum(axis=1), 1) +assert all(label in schema.values for label in model.predict_label(v)) +assert len(fit.state.model.terms) == 3 +``` + +Multiclass.geometry exposes weighted mean softmax loss and unweighted gradient +and diagonal bound arrays [N, K]. The bound is 2p(1-p), a diagonal upper bound +on the exact softmax Hessian, not the exact Hessian itself. The row-field adapter +applies original weights once. Initialization uses zero logits (uniform probabilities +before offsets), and every declared class must occur in training. Offsets enter +geometry once and remain outside accepted raw caches; supply inference offsets +explicitly. Fixed and backtracking steps use the common transaction machinery. + +Vector components also work independently of classification: + +- vector_newton binds gradient/diagonal-curvature arrays [N, L] to a problem. +- vector_score sums channel gains and charges one split penalty. vector_leaf + solves each regularized diagonal direction. Configure both with the same lambda. +- vector_feasible requires positive child curvature in every channel and nonempty + children, with an optional minimum child curvature. +- Each grower accepts separate leaf_fields from the same problem. A caller can + supply projected split statistics while fitting leaves from full statistics. + The caller owns the projection; this is not an automatic sketching policy. +- Tree.predict returns [N, L]. TreeTerm maps these outputs through an explicit + [L, K] matrix and a scalar coefficient into model raw space. + +The openboost-tree-v3 format stores vector payloads and rejects earlier formats. +Nested model artifacts preserve class labels and validate output-map dimensions; +loading requires no training objective. Numeric, categorical, missing and unseen +inputs use the same routing as scalar trees. + +Independent tests cover all three growth policies, projected splits with full +leaves, three softmax rounds, offsets, rejected updates and fresh-process +probability/label persistence. These are correctness checks, not real classification +quality evidence. Full A6 multi-output workflows, specialized leaves, CUDA and +external-library quality/performance comparisons remain required work. diff --git a/docs/v1/multioutput.md b/docs/v1/multioutput.md new file mode 100644 index 0000000..9accfe2 --- /dev/null +++ b/docs/v1/multioutput.md @@ -0,0 +1,54 @@ +# Multi-output squared regression + +multi_squared fits numeric targets [N,K] with matching raw width. It supports +independent scalar trees or one shared vector tree per round. All K updates +use the same accepted snapshot and commit or reject together. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import multi_squared +from openboost.multioutput import TargetScale, MultiOutputModel + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[0, 3], [2, 8], [1, 2], [6, 1]], x.row_ids) +valid = Problem(v, [[1, 4], [4, 2]], v.row_ids) +scaling = TargetScale.fit(train) +fit = multi_squared(scaling.transform(train), scaling.transform(valid), + context=RunContext("multioutput-demo", 7), mode="shared", rounds=3) +model = MultiOutputModel(fit.state.best_model, scaling) +prediction = model.predict(v) +rmse = np.sqrt(np.average((prediction-valid.target)**2, axis=0, weights=valid.weight)) +assert prediction.shape == (2, 2) and rmse.shape == (2,) +``` + +MultiSquared uses the sum of output half-squared errors, averaged by original +row weights. Its mse method reports weighted error for every target separately; +take square roots for per-target RMSE. Scalar K=1 agrees with the ordinary squared +recipe. Censored target kinds and classification schemas are rejected explicitly. + +mode="independent" fits K scalar trees with separate topology. mode="shared" +sums vector gains and fits full-dimensional leaves on common topology. grower +selects depthwise, best_first or symmetric. Optional projection [K,S] in shared +mode transforms split gradients by P and diagonal curvature by P**2; leaves still +use full K statistics. The caller owns the projection and its scientific meaning. +There is no automatic sketch selection or speed claim. + +TargetScale.fit uses training-only weighted means and population standard +deviations. Targets constant over positive-weight rows use scale one and a +persisted constant flag. No validation statistics are fitted. transform scales +targets and offsets consistently; absent offsets give zero weighted base up to +roundoff. With offsets, the base corrects the weighted residual mean. + +The raw recipe performs no implicit target standardization. For the A6 workflow, +fit the scaler on training, transform training/validation, and wrap the selected +model in MultiOutputModel. The wrapper restores original units and accepts +original-unit inference offsets. Its versioned artifact embeds model, means, +scales and constant flags. Target columns retain their declared positional order. + +Tests cover three rounds under all policies/modes, weights/offsets, target +permutation, K=1, constant targets, foreign target semantics, atomic rejection, +and fresh-process mixed/missing/unseen prediction in both modes. Real Parkinsons +subject-split evaluation, standardized aggregate plus per-target quality, +shared preparation/stopping and CUDA remain required work. diff --git a/docs/v1/normal.md b/docs/v1/normal.md new file mode 100644 index 0000000..1311f6c --- /dev/null +++ b/docs/v1/normal.md @@ -0,0 +1,58 @@ +# Joint Normal distributional boosting + +A scalar observation can have multiple predicted parameters. Declare +`Problem(..., raw_width=2)` for Normal's raw mean and log-scale columns. Targets +remain `[N, 1]`; optional offsets must be `[N, 2]`. The same runtime and mapped +scalar tree terms used by squared boosting update these parameters jointly. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import normal +from openboost.objectives import Normal + +x = NumericData([[0], [1], [2], [3], [4], [5]], np.arange(6), ("x",)) +v = NumericData([[0.5], [2.5], [4.5]], [10, 11, 12], ("x",)) +train = Problem(x, [[-3], [-1], [0], [1], [3], [6]], x.row_ids, raw_width=2) +valid = Problem(v, [[-2], [0.5], [4]], v.row_ids, raw_width=2) +fit = normal(train, valid, context=RunContext("normal-demo", seed=7), + rounds=3, bins=6, mode="natural") +raw = fit.state.best_model.predict(v) +parameters = Normal.parameters(raw) # columns: mean, positive scale +assert parameters.shape == (3, 2) and np.all(parameters[:, 1] > 0) +assert np.isfinite(Normal.loss(valid, raw)) +``` + +`Normal.geometry(problem, raw)` exposes weighted mean NLL, unweighted ordinary +likelihood gradient and Fisher diagonal `[N, 2]`. For residual r=mean-target and +precision p=exp(-2 log-scale), the gradient is `(r*p, 1-r*r*p)` and Fisher diagonal +is `(p, 2)`. It is not the observed Hessian. `diagonal_direction` returns `-g` in +ordinary mode or `-g/(F+damping)` in natural mode. Ordinary mode rejects nonzero +damping. No dense per-row metric is needed for Normal's diagonal Fisher. + +`least_squares(problem, direction_column)` creates G=-w*z and H=w from an +unweighted direction. These regression curvatures are distinct from Fisher +entries. Both parameter trees fit the same accepted snapshot, and one coefficient +commits or rejects both terms. The default uses six-trial backtracking with strict +training NLL decrease. `step="fixed"` commits finite candidates. Geometry, trees, +weights, offsets, best snapshots and persistence use public shared components. + +With no offsets, initialization uses training weighted mean and log standard +deviation, floored by `minimum_scale` (default 1e-6). With offsets, the mean uses +weights multiplied by exp(-2 log-scale-offset); the variance uses those residual +squares normalized by original weight mass. The floor applies to initial base +scale only. Later scale/precision underflow or overflow is rejected without +clipping. Backtracking continues after invalid numerical trials, recording error +types in `NormalStep.failures`; wrong schemas fail before any trials. + +`Model.save/load` stores raw ensemble state and mappings. `Normal.parameters` +converts raw mean/log-scale to mean/scale without training data. For observation +offsets, pass them to `Model.predict(..., offset=...)` before converting; objective +loss receives unoffset raw caches and applies Problem offsets once. The model is +a raw predictor and does not persist a distribution tag or calibrated intervals. + +This is a joint-update CPU recipe, not full NGBoost parity. Ordered parameter +updates, additional distributions and CUDA remain required later work. +[Formula](formula-runs.md) now probes full GGN geometry through shared components. No real-dataset quality or speed advantage is claimed. +Per-round traces retain arrays, and trial validation currently recomputes ensemble +predictions. Formula and heterogeneous sequential runs now provide the next construction probe. diff --git a/docs/v1/numeric-ops.md b/docs/v1/numeric-ops.md new file mode 100644 index 0000000..cc7ea06 --- /dev/null +++ b/docs/v1/numeric-ops.md @@ -0,0 +1,63 @@ +# Numeric preparation and scalar operations + +The initial B04 slice exposes CPU binning, named row fields, histograms, candidate +statistics, scoring/feasibility callbacks, routing and scalar Newton leaves. +The [depthwise grower](trees.md) composes these operations into numeric trees. + +```python +import numpy as np +from functools import partial +from openboost import NumericData, Problem +from openboost.binning import Binning +from openboost.stats import newton +from openboost.ops import histogram, candidates, choose, feasible, partition, newton_leaf + +x = NumericData(np.arange(6)[:, None], np.arange(100, 106), ("x",)) +p = Problem(x, np.zeros((6, 1)), x.row_ids) +b = Binning.fit(x, bins=6).transform(x) +fields = newton(p, [-6, 1, 1, 1, 1, 2], np.ones(6)) +fields = fields.add_independent("a", [1, 0, 1, 0, 1, 0]) +fields = fields.add_independent("b", [0, 1, 0, 1, 0, 1]) +options = candidates(histogram(b, fields)) +best = choose(options, legality=partial(feasible, min_information={"a": 1, "b": 1})) +left, right = partition(b, None, best) +for rows in (left, right): + assert fields.values[rows, 2:].sum(axis=0).min() >= 1 + leaf = newton_leaf(histogram(b, fields, rows).total, fields.names) + assert np.isfinite(leaf) +``` + +Fit binning on training data, then reuse it on validation/inference data. Cuts use +linear empirical quantiles, duplicate removal and the rule value <= cut goes left. +The minimum observed value may be a cut. Out-of-range observations use end bins. +Constant/all-missing columns have no internal cut; all-missing training columns +produce no candidates. A constant observed column may still split missing rows. +If finite inputs overflow quantile interpolation, fitting fails explicitly rather +than silently dropping those cuts; rescale such extreme features. +Codes are owned feature-major int32 arrays `[F, N]`; missingness is a separate +boolean array. The binning identity distinguishes transformed views of the same +raw data. No weights enter quantile fitting. + +`newton(problem, gradient, curvature)` takes unweighted scalar `[N]` derivatives +and applies the original `[N]` training weights once. It does not calculate the +objective derivatives or support vector leaves yet. General `RowFields` declare +names and per-column weight roles. `apply_weight` rejects already weighted fields; +`add_independent` leaves auxiliary fields unweighted. Independent information must +be supplied with the statistical meaning required by the caller's constraint. +Metadata prevents API-level reweighting but cannot prove arbitrary plugin math. + +Histograms use selected original positional rows, not rescaled parent summaries. +They preserve physical counts separately from weighted curvature and sum all named +fields. Candidates use histogram prefix/suffix sums and both missing routes. +`score` uses G²/(2(H+lambda)) and one split penalty; `feasible` requires nonempty +children and positive curvature plus declared minima. Information minima require +independent fields. `choose` accepts replacement scoring and legality functions; +it chooses the highest strictly positive gain with exact lexicographic ties by +feature, threshold and missing direction (right first). Invalid custom scores fail. + +`partition` returns original positional rows; their source IDs are +`b.data.row_ids[rows]`. Applying a candidate to a different binning or routed row +sequence fails. Leaf solving consumes already-weighted sums without weighting +again. Operations are synchronous CPU NumPy/Python; no CUDA, sparse memory guarantee, +fusion, throughput or complete boosting result is claimed. The public depthwise grower uses +these operations with verified numeric tree persistence. diff --git a/docs/v1/poisson.md b/docs/v1/poisson.md new file mode 100644 index 0000000..b10ffa6 --- /dev/null +++ b/docs/v1/poisson.md @@ -0,0 +1,53 @@ +# Poisson counts and exposure + +The CPU Poisson recipe models integer counts with mean +exposure * exp(raw + offset). Raw model output is a log rate per unit exposure. +Declare strictly positive exposure as an aligned [N,1] structure role; use ones +for unit exposure. Original sample weights are independent of exposure. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import poisson +from openboost.outputs import poisson_mean + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[0], [1], [3], [5]], x.row_ids, + structure={"exposure": [[0.5], [1], [2], [1]]}) +valid = Problem(v, [[1], [2]], v.row_ids, + structure={"exposure": [[1], [0.5]]}) +fit = poisson(train, valid, context=RunContext("poisson-demo", 7), rounds=3) +means = poisson_mean(fit.state.best_model.predict(v), [1, 0.5]) +assert np.isfinite(means["rate"]).all() +np.testing.assert_allclose(means["count_mean"], means["rate"] * [1, 0.5]) +``` + +Poisson.geometry returns weighted mean negative log likelihood (including the +log-factorial constant) and unweighted gradients/curvatures. Curvature equals +the count mean. The standard Newton adapter applies original weights once. +There is no implicit curvature floor or additional Poisson step constraint. + +Initialization maximizes the constant-rate likelihood with the declared offsets: +log(sum(weight*count)) - log(sum(weight*exposure*exp(offset))). +The sums are evaluated in log space. When all positive-weight counts are zero, +minimum_rate supplies an explicit positive raw-rate initializer (default 1e-6); +it is not a prediction floor. Fixed/backtracking steps and validation best-model +selection use Poisson likelihood. + +Offsets are additional log-rate offsets. Do not also put log exposure in offset +when supplying exposure separately. Accepted raw caches exclude both roles. +Supply inference offsets explicitly through model.predict, then call +poisson_mean(raw, exposure) to obtain named rate and count_mean arrays. Doubling +exposure at fixed raw output doubles count mean and leaves the unit rate unchanged. + +Generic model artifacts store raw trees/base, not exposure, training offsets or +an output-family tag. The caller must retain the declared Poisson inference +transform and supply new exposure. Fresh-process tests verify that composition +on numeric/categorical/missing/unseen inputs. + +Fractional or negative counts, nonpositive/missing exposure, unknown structure, +nonfinite geometry and means outside positive float64 range fail explicitly. +Tests verify independent geometry, offset-aware initialization, three-round +trees, zero counts and exposure scaling. Real A7 deviance/calibration comparisons, +Gamma/A8, Tweedie/composition/A9, AFT/A10 and CUDA remain required work. diff --git a/docs/v1/preparation.md b/docs/v1/preparation.md new file mode 100644 index 0000000..fad7b25 --- /dev/null +++ b/docs/v1/preparation.md @@ -0,0 +1,49 @@ +# Shared CPU training preparation + +PreparedData fits training binning and codes once for immutable feature data. +Every built-in recipe accepts prepared alongside the matching bins setting. +Targets, weights, offsets, objectives and run state remain separate. + +```python +from openboost import NumericData, Problem, RunContext +from openboost.binning import PreparedData +from openboost.recipes import squared +from openboost.runs import RunSpec, run_many + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[0], [2], [1], [6]], x.row_ids) +valid = Problem(v, [[1], [4]], v.row_ids) +prepared = PreparedData(x, bins=4) +jobs = [ + RunSpec(RunContext(f"model-{i}", i), train, valid, squared, + {"bins": 4, "rounds": i+1}, prepared=prepared) + for i in range(8) +] +outcomes = run_many(jobs) +assert all(item.error_type is None for item in outcomes) +``` + +Preparation identity includes training data content/schema/row IDs, bin capacity +and fitted transformer/code identity. Supplied preparation must match both the +recipe's feature data and bins configuration, even if two capacities happen to +produce identical cuts. Mismatches fail rather than refitting silently. +Different targets/weights over identical features may share preparation. + +RunSpec has a dedicated prepared field; it cannot be hidden in scalar options. +run_many forwards it to the recipe and records individual failures. Without +preparation, recipes fit their own training binning as before. There is no +implicit cache keyed by object address or task name. + +This reuses the training codes consumed by histogram growth. Model inference +still transforms input rows when predicting, and validation preprocessing is +not cached by PreparedData. The record is an in-memory CPU object, not a +serialized training-resume checkpoint or a device workspace. + +Tests compare M=1/8/32 heterogeneous jobs with independent, reversed and +regrouped execution, prohibit refitting after preparation, and verify distinct +raw caches, config mismatch rejection and continued execution after a failure. +These are equivalence checks, not timing/fusion evidence. +[Independent validation stopping](stopping.md) is also available through scalar +patience/min_delta run options. Real model selection, GPU batching and +end-to-end cost remain open. diff --git a/docs/v1/quantile.md b/docs/v1/quantile.md new file mode 100644 index 0000000..9cc97b8 --- /dev/null +++ b/docs/v1/quantile.md @@ -0,0 +1,50 @@ +# Quantile and penalized residual leaves + +Quantile boosting uses pinball gradients and unit pseudo-curvature to choose +splits, then fits each leaf from its routed current residuals and original +weights. Pseudo-curvature is not the Hessian of pinball loss. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import quantile +from openboost.tree import best_first + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[0], [2], [1], [6]], x.row_ids, weight=[1, 2, 1, 3]) +valid = Problem(v, [[1], [4]], v.row_ids) +fit = quantile(train, valid, context=RunContext("quantile-demo", 7), + q=0.8, rounds=3, grower=best_first, penalty=2, anchor=0) +assert np.isfinite(fit.state.best_model.predict(v)).all() +``` + +Initialization is the weighted quantile of target minus offset. Each round +recomputes residuals from the accepted model, including the offset once. +Fixed or backtracking steps and validation selection use weighted mean pinball +loss. The gradient at an exact target/prediction tie is -q, matching the declared +reference subgradient convention. + +All three growth policies accept row_leaf(view, total, names) together with +leaf_context=ResidualContext(problem, residual). The context owns aligned +unweighted residuals. Each immutable ResidualView carries global row IDs, +original weights and residuals for exactly the routed rows. total and names +describe the additive leaf statistics. Context identity must match the split +problem; custom additive and routed leaf solvers cannot be supplied together. + +quantile_leaf(view, q=...) selects the leftmost weighted quantile when penalty=0. +With penalty>0 it minimizes the sum of original-weight pinball losses plus +penalty*(value-anchor)**2/2. A monotone subgradient scan finds the unique +minimizer at a residual breakpoint or between breakpoints. Weight mass is not +normalized away from this penalty. Zero-mass leaves return the anchor only for +positive penalty; unpenalized zero-mass leaves fail explicitly. + +The recipe's reg_lambda regularizes split scoring. Its separate penalty and +anchor configure leaf fitting; they do not change the validation metric or +add a model-wide training penalty. An anchor with zero penalty is rejected. +Depthwise, best-first and symmetric policies share this contract through grower. + +Inference stores the solved scalar leaf values in the existing tree format, +without residuals, training rows or a training objective. These tests establish +CPU mechanics and D3 solver correctness, not real A5 quality, quantile coverage, +noncrossing guarantees, agent-author effort, CUDA or performance results. diff --git a/docs/v1/ranking.md b/docs/v1/ranking.md new file mode 100644 index 0000000..83eca9a --- /dev/null +++ b/docs/v1/ranking.md @@ -0,0 +1,54 @@ +# Query-local ranking + +Ranking consumes scalar nonnegative integer relevance and explicit query roles. +Query IDs are aligned integer codes stored in structure, never appended to features. +Optional query_weight is repeated on each row and must be constant within a query. +Non-unit row weights are rejected. Default query weights are one. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import ranking + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +train = Problem(x, [[0], [2], [1], [0]], x.row_ids, + structure={"query": [[0], [0], [1], [1]]}) +valid = Problem(v, [[0], [2]], v.row_ids, + structure={"query": [[2], [2]]}) +fit = ranking(train, valid, context=RunContext("ranking-demo", 7), + rounds=3, lambdas=True, k=10) +scores = fit.state.best_model.predict(v) +assert scores.shape == (2, 1) and np.isfinite(scores).all() +``` + +Ranking(lambdas=False).geometry(problem, raw) returns pairwise logistic loss and +row gradient/diagonal-curvature vectors. For each query, it enumerates every pair +with unequal relevance, orients the higher-relevance row first, and divides each +pair's weight by that query's eligible pair count. The query weight multiplies +this factor; tied relevance contributes no pairs. Each pair adds opposite gradients +and equal diagonal curvature to its two rows. + +With lambdas=True, the factor also includes the absolute change in NDCG@k from +swapping the pair. Gains are 2**relevance - 1; ranks sort by descending score, with +row ID breaking ties. The geometry call freezes these delta weights rather than +differentiating them. Each boosting round recomputes them from the current scores. +Offsets enter scores exactly once. Query-local geometry reduces to ordinary +scalar Newton fields and uses the existing growth and transaction operations. + +The recipe uses finite fixed steps. Validation selects best_model using one minus +query-weighted mean NDCG@k; it does not select by the moving lambda-weighted pair +loss. Zero-ideal-DCG queries have NDCG one. All-zero query weights are rejected. +Backtracking, explicit pair weights and pair sampling are not accepted by this +initial API. Enumeration needs quadratic memory and time per query, so it is a +correctness implementation, not a scalable ranking performance claim. + +Inference artifacts return raw scores without training query roles. Supply offsets +explicitly when needed; downstream ranking should use the same row-ID tie rule. +Keep complete queries in separate train/validation/test partitions for real +evaluation. The low-level recipe does not certify dataset partition independence. + +Independent tests verify pair geometry, logistic finite differences, query +isolation, rank tie permutations, three-round tree composition, offsets and +fresh-process persistence. Real A4 evaluation, quantile/penalized leaves, CUDA +and external-library quality/performance comparisons remain open. diff --git a/docs/v1/results.md b/docs/v1/results.md new file mode 100644 index 0000000..74630a0 --- /dev/null +++ b/docs/v1/results.md @@ -0,0 +1,29 @@ +# Shared recipe results + +`openboost.results.RecipeResult` is a structural protocol for completed recipes. +The scheduler accepts any object exposing: + +- `state`: an AcceptedState belonging to the requested context and train/validation problems. +- `steps`: a tuple with one entry per completed outer round. Each entry's contents + belong to the recipe; an ordered recipe may store a tuple of parameter substeps. +- `stop`: a completed StopState, with reason `budget` or `patience`. + +Authors need not inherit a base class or convert their result into the built-in +FitResult. The scheduler retains the original object and its diagnostic payloads. +`validate_result(result, context=..., train=..., validation=...)` exposes the same +checks for callers outside run_many. A declared protocol alone is insufficient: +runtime validation checks field types, matching identities, completion and trace +length. Invalid results become retained per-run errors; other jobs continue. + +Accepted-state version counts model commits, not outer rounds. Two parameter +commits can correspond to one trace entry and one patience observation. An +unfinished stop record cannot represent a successful run. This is an in-memory +contract, not a resume format or process-isolation boundary. External callbacks +must respect immutable input/state ownership; the scheduler does not copy or +interpret arbitrary diagnostic payloads. + +The ordered-update development wheel now runs unchanged beside built-in squared +recipes. Installed checks cover M=1/8/32, shared preparation without refitting, +different validation stop rounds, reversed/regrouped execution, retries and an +isolated malformed result. These are sequential correctness checks, not batching, +speed, real model selection or a complete D5/E5 result. diff --git a/docs/v1/squared.md b/docs/v1/squared.md new file mode 100644 index 0000000..d0469ca --- /dev/null +++ b/docs/v1/squared.md @@ -0,0 +1,54 @@ +# Complete scalar squared boosting + +`openboost.recipes.squared` composes `Squared` geometry, once-weighted row fields, +train-fitted binning, depthwise trees, mapped terms and immutable run transactions. +It returns final accepted state and per-round evidence. It is a CPU correctness +path, with no measured quality or speed parity claim. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import squared + +train_x = NumericData([[0], [1], [2], [3]], [10, 11, 12, 13], ("x",)) +valid_x = NumericData([[0.5], [2.5]], [20, 21], ("x",)) +train = Problem(train_x, [[-3], [-1], [1], [3]], train_x.row_ids) +valid = Problem(valid_x, [[-2], [2]], valid_x.row_ids) +fit = squared(train, valid, context=RunContext("demo", seed=7), + rounds=3, learning_rate=0.5, bins=4) +assert fit.state.version == 3 +assert fit.steps[-1].loss_after < fit.steps[0].loss_before +prediction = fit.state.best_model.predict(valid_x) +assert prediction.shape == (2, 1) and np.isfinite(prediction).all() +``` + +The objective uses weighted mean half-square loss and initializes the raw base +with the training weighted mean of `target - offset`. Derivatives include offsets; +raw state never stores observation offsets. Statistics apply original training +weights once. Binning uses training features only. Validation selects an immutable +`best_model`; the final accepted `model` can differ. Both are inference artifacts. + +`step="fixed"` commits each finite candidate even if loss rises. With +`step="backtracking"`, at most `max_trials` (1–6) coefficients are tried, halving +`learning_rate` each time. Strict training-loss improvement accepts a trial; full +rejection leaves terms, caches, best model and version unchanged. A learner is +fitted once per round. The initial CPU implementation recomputes ensemble +predictions during trial validation; prediction caching remains future work. +Nonfinite fixed-step arithmetic raises; backtracking rejects an invalid numerical +trial and continues at a smaller coefficient. Structural errors raise immediately. + +The defaults expose `max_depth`, `max_leaves`, `reg_lambda`, `min_child_h` and +`split_penalty`. `learner(binned_data, weighted_fields)` can replace the default +learner; configure custom growth in that callable and leave recipe growth options +at defaults. The recipe rejects conflicting nondefault growth options. Low-level +code can instead use `Squared.fields`, `depthwise`, `TreeTerm`, `propose_terms`, +`preview` and `resolve` directly, including explicit output mappings. + +This recipe accepts scalar `[N, 1]` targets and raw_width=1. The +[Normal recipe](normal.md) provides two-parameter distributional geometry. +[Binary classification](binary.md) is available. Multiclass, specialized targets, +vector learners, callbacks/early stopping, +CUDA remains a future slice. [Categorical inputs](categorical.md) are supported. Best-first and symmetric +growers can be substituted through the learner argument. Unsupported arguments fail. +The trace retains per-round arrays for correctness inspection, and is not a +memory-efficient large-workload implementation or a training-resume checkpoint. diff --git a/docs/v1/stopping.md b/docs/v1/stopping.md new file mode 100644 index 0000000..52d425e --- /dev/null +++ b/docs/v1/stopping.md @@ -0,0 +1,62 @@ +# Independent validation stopping + +Every CPU recipe accepts `patience=None` and `min_delta=0.0`. Positive integer +patience enables early stopping; None uses the complete round budget. A nonzero +min_delta requires enabled patience. Invalid configuration fails even at zero rounds. + +```python +from openboost import NumericData, Problem, RunContext +from openboost.recipes import squared +from openboost.stopping import StopState + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +train = Problem(x, [[0], [1], [2], [3]], x.row_ids) +valid = Problem(x, [[3], [2], [1], [0]], x.row_ids) +fit = squared(train, valid, context=RunContext("stop-demo", 7), + rounds=20, patience=2) +assert fit.stop.reason == "patience" +assert fit.stop.completed_rounds == len(fit.steps) == 2 +assert fit.state.version == 2 +assert fit.state.best_model.terms == () + +# External algorithm loops use the same public operation. +stop = StopState.start(10.0, rounds=8, patience=2, min_delta=1.0) +stop = stop.observe(9.0) # Exact threshold does not reset patience. +stop = stop.observe(8.5) # Improvement from 10 exceeds the threshold. +assert stop.stale_rounds == 0 and stop.reason is None +``` + +The small example deliberately uses opposite targets on the same feature rows +to expose stopping behavior; it is not a real evaluation split. + +StopState is immutable and separate from AcceptedState. Initial validation is +the baseline and consumes no round. Once per completed outer round, observe the +current finite validation score, with smaller scores better (ranking uses negative +NDCG). Strict improvement must exceed min_delta relative to the last qualifying +improvement. Ties and insufficient improvements increment stale_rounds; qualifying +improvement resets it. Stop at patience consecutive stale rounds or the round +budget. A simultaneous limit reports `patience`; zero rounds reports `budget`. +Further observations after termination raise an error. + +Backtracking still uses training loss for step acceptance. Individual search +trials and ordered substeps must not advance the stopping clock. A fully rejected +outer round observes the unchanged model once and consumes patience. Accepted-state +version counts commits, not completed rounds. The existing per-step coefficients +record attempted step sizes; they do not determine patience. + +`fit.state.model` is the final accepted model; `fit.state.best_model` is the strict +validation minimum, including the initial model. Best-model selection is independent +of min_delta, so small improvements can update best_model without resetting patience. +`fit.stop` reports completed_rounds, stale_rounds, reference_score, last_score and +reason. It is an in-memory progress record, not a training-resume checkpoint. + +RunSpec passes scalar patience/min_delta options unchanged. Each run owns its stop +record even when it shares PreparedData. Reordering or retrying stable run IDs +preserves results; invalid configuration or a nonfinite initial/observed metric +fails that run and run_many retains its error. Candidate numerical failures during +backtracking retain the recipe's existing rejection behavior. + +Tests cover all twelve recipes, hand-calculated threshold sequences and M=1/8/32 +heterogeneous scalar/Normal runs with different actual validation stop rounds, +failed runs, retries and regrouping. This establishes CPU state semantics, not +real model-selection quality, GPU batching, fusion or a speed improvement. diff --git a/docs/v1/trees.md b/docs/v1/trees.md new file mode 100644 index 0000000..15db988 --- /dev/null +++ b/docs/v1/trees.md @@ -0,0 +1,87 @@ +# Tree growth policies + +The CPU growers assemble public histogram, candidate, choice, routing and scalar/vector +leaf operations. Each accepts replacement scoring, legality and leaf functions. +Child IDs are explicit and stable. + +- `depthwise`: choose positive-gain splits within each layer; a leaf cap selects + higher gains, with node ID and condition breaking ties. +- `best_first`: a heap selects the highest-gain active leaf across depths, then + node ID and condition. Only newly created children need candidate evaluation. +- `symmetric`: choose one common condition legal in every active leaf, maximizing + total gain across the complete layer. Individual gains may be negative; their + total must be positive. A leaf budget must admit the entire next layer. + +Scoring and legality callbacks should be pure functions of candidate statistics +and immutable configuration. Unchanged best-first candidates retain cached scores; +changing behavior by call order is unsupported. All three policies preserve +exact condition tie rules and use the same inference format. + +```python +import tempfile +from pathlib import Path +import numpy as np +from openboost import NumericData, Problem +from openboost.binning import Binning +from openboost.stats import newton +from openboost.tree import depthwise, Tree + +x = NumericData([[0], [1], [2], [3], [np.nan]], [10, 11, 12, 13, 14], ("x",)) +p = Problem(x, np.zeros((5, 1)), x.row_ids) +b = Binning.fit(x, bins=4).transform(x) +fields = newton(p, [-4, -2, 1, 3, 2], np.ones(5)) +tree = depthwise(b, fields, max_depth=2, max_leaves=3) +assert tree.predict(x).shape == (5, 1) +with tempfile.TemporaryDirectory() as directory: + path = Path(directory) / "tree.json" + tree.save(path) + restored = Tree.load(path) + np.testing.assert_array_equal(restored.predict(x), tree.predict(x)) +``` + +`scoring(candidate)` and `legality(candidate)` have the public operation contracts. +Scoring executes once per legal candidate, including when ranking nodes within a +layer. `leaf(total, names)` returns a finite scalar or nonempty vector from weighted +additive sums. Every node must return the same width. Optional `leaf_fields` from +the same problem separates split statistics from full leaf statistics. Vector +Newton adapters and callbacks are described in [multiclass and vectors](multiclass.md). +For custom Newton regularization, configure both scoring and leaf solving with +the same regularizer. A custom legality callback must preserve nonempty children; +the grower rejects an admitted empty child. Callbacks should be deterministic. For residual-based solvers, use the paired +row_leaf and leaf_context arguments described in [quantile leaves](quantile.md). + +`Tree` owns immutable int32 feature/threshold/child arrays, boolean missing +routes and float64 values shaped [nodes, L]. Leaf children, feature and threshold use -1; prediction +uses explicit indices. Construction/load rejects cycles, shared or unreachable +nodes, invalid indices, schema mismatches and nonfinite leaves. Artifacts contain +numeric cuts, typed category dictionaries and ordered feature names. Numeric +conditions use <=, categorical conditions use equality. The saved transformer +defines condition kinds and routes unknown tokens as missing. + +`predict` returns raw learner output `[N, L]` (L=1 for scalar leaves). It applies no base, coefficient +or observation offset. Mapped tree terms integrate with the transaction model and the +[squared](squared.md) and [Normal](normal.md) recipes. Artifacts +are for inference, not training resumption. Linear leaves, +CUDA and performance claims remain outside this slice. + + +Recipes accept a custom learner, so growth policy changes do not require editing +the objective or transaction loop: + +```python +from functools import partial +from openboost import NumericData, Problem, RunContext +from openboost.recipes import squared +from openboost.tree import best_first, symmetric + +x = NumericData([[0, 0], [0, 1], [1, 0], [1, 1]], [1, 2, 3, 4], ("a", "b")) +p = Problem(x, [[-3], [-1], [1], [3]], x.row_ids) +for grow in (best_first, symmetric): + result = squared(p, p, context=RunContext(grow.__name__, 1), rounds=2, + learner=partial(grow, max_depth=3, max_leaves=4)) + assert result.state.version == 2 +``` + +This arithmetic example reuses train/validation data; real evaluation needs the +prescribed distinct partitions. These growth policies do not establish LightGBM +or CatBoost feature/quality parity. [Categorical support](categorical.md) uses the same three policies. diff --git a/docs/v1/tweedie.md b/docs/v1/tweedie.md new file mode 100644 index 0000000..dfb7256 --- /dev/null +++ b/docs/v1/tweedie.md @@ -0,0 +1,57 @@ +# Tweedie nonnegative means + +Tweedie mean regression accepts zero and positive scalar targets, with variance +power fixed per fit strictly between one and two. Raw predictions are log means; +positive_mean converts them to means. Dispersion and the full compound +distribution are not fitted. + +```python +import numpy as np +from openboost import NumericData, Problem, RunContext +from openboost.recipes import tweedie +from openboost.outputs import positive_mean + +x = NumericData([[0], [1], [2], [3]], [0, 1, 2, 3], ("x",)) +v = NumericData([[0.5], [2.5]], [10, 11], ("x",)) +exposure = np.array([0.5, 1, 2, 1]) +period_total = np.array([0, 2, 4, 8]) +train = Problem(x, (period_total/exposure)[:, None], x.row_ids, weight=exposure) +valid = Problem(v, [[0], [4]], v.row_ids, weight=[1, 0.5]) +fit = tweedie(train, valid, context=RunContext("tweedie-demo", 7), + power=1.5, rounds=3) +annualized_mean = positive_mean(fit.state.best_model.predict(v)) +period_mean = annualized_mean * [1, 0.5] +assert np.isfinite(period_mean).all() +``` + +For power p and total log mean f=raw+offset, the objective is +y*exp((1-p)*f)/(p-1) + exp((2-p)*f)/(2-p). +Its gradient is exp((2-p)*f)-y*exp((1-p)*f), and its curvature is +(2-p)*exp((2-p)*f)+(p-1)*y*exp((1-p)*f). +The zero-target term is exactly zero. Original weights are applied once by +the shared Newton adapter. + +The constant initializer accounts for offsets using the log ratio of +sum(w*y*exp((1-p)*offset)) to sum(w*exp((2-p)*offset)). +All-zero positive-weight targets use the explicit minimum_mean initializer +(default 1e-6), not a prediction floor. Fixed/backtracking steps and validation +selection use weighted objective values. This objective has the same prediction +ordering as Tweedie deviance at fixed targets/power, but is not a reported +deviance or full normalized distribution likelihood. + +For A9 annualized loss, divide period amounts by exposure and use exposure +weights, including explicit extra business weights if required. Do not also +add log exposure to the offset. Convert annualized means to period means by +multiplying by exposure at inference. Other units require an explicit contract. +Unknown structure roles, negative targets, invalid powers and unrepresentable +positive terms are rejected. + +Generic inference artifacts store raw trees/base; callers retain the output +transform and unit conversion. Fresh-process tests cover mixed, missing and +unseen features plus offsets. Power affects fitting and evaluation but not the +exp(raw) inference transform. + +Independent tests cover three powers, zero-target finite differences, intercepts, +three rounds, annualized weights, rejected steps and persistence. Frequency– +severity composition, real A9 quality, calibrated tails, AFT/A10 and CUDA remain +separate required work. diff --git a/examples/README.md b/examples/README.md index 077eb49..59791b6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,6 @@ +> **Historical implementation:** this page describes the retired pre-rebuild API. +> Reproduce at Git revision `50acfc6`; see the repository README and `v1-sprints/` for current v1 status. + # OpenBoost Examples Runnable scripts. Distributional regression first; mean-regression GBDT after. diff --git a/examples/extensions/AUTHOR_TASK.md b/examples/extensions/AUTHOR_TASK.md new file mode 100644 index 0000000..17d3789 --- /dev/null +++ b/examples/extensions/AUTHOR_TASK.md @@ -0,0 +1,50 @@ +# Independent author trial: task and record + +Status: prepared, **not attempted by an external author**. Repository-maintained +examples are not third-party adoption. Do not fill this in on an author's behalf. + +## Task handed to the author + +Build a separate installable package providing a Laplace distribution objective +with location and log-scale channels. Choose and document a defensible nonnegative +effective curvature, including how you handle the location loss's nonsmooth point. +Use sample weights. Add a leaf rule limiting absolute updates and a predetermined +per-channel schedule. Run at least two boosting rounds through the public +`openboost.experimental` API without private imports or core changes. + +Supply an independent mathematical reference, CPU results, held-out NLL and a +calibration diagnostic on a dataset you can redistribute or identify publicly. +If CUDA is available, verify the same objective and training behavior there; +otherwise record that GPU validation was unavailable. Build and install your +wheel outside the repository. Save the model, uninstall the plugin and confirm +CPU raw prediction after loading in a fresh process. Record failures and requests +for help. No implementation or formulas are supplied with this task. + +Start with the experimental extension cookbook and API guide. The author may +consult public examples; record every resource and assistance interaction used. +Agree on a time budget before starting. Stopping without a correct result is a +valid outcome and must remain in the record. + +## Record completed by the author + +| Field | Observation | +|---|---| +| Date, author role, prior boosting/Python/CUDA experience | | +| Agreed time budget; environment and dependency versions | | +| Dataset/version/hash; split seed and target preprocessing | | +| Core version/SHA; plugin source and wheel hashes | | +| Start time; first installed import; first mathematically correct fit | | +| Total active time; environment/setup time; blocked time | | +| Assistance count; question and response for each interaction | | +| Documentation/examples consulted | | +| Private imports or core changes required (include attempted ones) | | +| Independent oracle, tolerances, failures, CPU outputs | | +| CUDA hardware, actual execution report, parity or reason unavailable | | +| Held-out NLL/calibration and fit/predict timing, including scope | | +| Plugin-free inference result in a fresh interpreter | | +| Would you depend on OpenBoost in an independent package? Why? | | +| Hardest step; missing abstraction; next change you would request | | + +Keep raw logs/artifacts with permission to share them. An unsuccessful or +unfinished attempt is evidence too. This form does not authorize contacting an +author, posting results or publishing their information. diff --git a/examples/extensions/README.md b/examples/extensions/README.md new file mode 100644 index 0000000..72a7424 --- /dev/null +++ b/examples/extensions/README.md @@ -0,0 +1,122 @@ +> **Historical implementation:** these extension packages use the retired API. +> Reproduce at revision `50acfc6`; they are not current v1 author-package evidence. +> See [CPU coverage audit](../../v1-sprints/035-cpu-coverage-audit.md). + +# Independent CPU/CUDA extension wheels + +Two small packages demonstrate three extension points without private imports, +core edits or a fork: + +- `normal_fisher`: independent weighted Normal NLL/Fisher objective and + `ChannelDecay` predetermined per-channel coefficients. +- `bounded_leaves`: `BoundedNewton` clips Newton leaf values during training. + +These are repository-maintained examples, not evidence of external adoption. +Version 0.2.0 declares NumPy/CuPy support. Real-device installed-wheel conformance +is a separate gate from the primitive and built-in adapter tests. Numerical fixtures are not quality benchmarks. + +## Reproduce the installation boundary + +From the repository with the development environment installed: + +```sh +uv run --no-sync python examples/extensions/verify_wheels.py /tmp/openboost-extension-evidence +``` + +The verifier builds OpenBoost and both extension wheels, creates a disposable +venv outside the repository, installs wheels without editable mode or PYTHONPATH, +checks installed module locations, runs independent package tests and the +combined example, then uninstalls both plugins. A new interpreter loads six +saved models and requires exact CPU raw predictions with neither plugin +importable. Only sanitized JSON and JUnit evidence leave the temporary directory. +Use a clean commit for recorded evidence; development runs explicitly record +the dirty source state. No files are uploaded or packages published. + +`requirements-cpu.txt` pins the tested Python 3.12 CPU environment. On Intel +macOS, copying the development environment's Numba 0.63.1 / llvmlite 0.46.0 +pins triggered an unsuccessful source build. This example instead tests the +compatible binary stack Numba 0.61.2 / llvmlite 0.44.0 / NumPy 2.2.6. +This does not change OpenBoost's project-wide dependency ranges. + +For manual installation, build all three wheels with `uv build --wheel` (use +`--out-dir` to collect them), create a new environment with `uv venv`, and use +`uv pip install --python -r examples/extensions/requirements-cpu.txt + `. Copy `demo.py` +to a directory outside the repository and run it with that environment's Python +and `OPENBOOST_BACKEND=cpu`. It saves `demo.ob` and prints the actual coefficients. +The verifier executes this same file; it is the runnable public example. + +## What the checks establish + +Finite differences of independent float64 weighted NLL verify both gradients; +analytic expected Fisher verifies effective curvature. Two rounds of depth-one +training are checked against exhaustive original-row splits and scalar Newton +updates, including the actual schedule and bound. Separate objective-only, +scheduled, bounded and combined fits distinguish the effects. The next-round +mean gradient changes after clipping. A real opposing-target validation set +triggers early stopping and checks tree/coefficient restoration. + +The objective initializes weighted mean/variance (variance floor 1e-6), weights +both gradients and curvature exactly once, and rejects unsupported extra targets. +No raw clipping is applied. Non-finite states and zero precision from extreme-scale underflow fail. Builder scope is numeric, +nonmissing, L2, full sampling, depth 0–8. Clipping retains the original split +criterion. Loading requires only OpenBoost; the saved model is inference-only. + +## Usability observations + +The packages require zero private OpenBoost imports and no core changes. The +bounded rule uses public `NewtonLeafRule`; objective math is independently +implemented rather than a built-in alias. Users explicitly select a builder to +attach a leaf rule and call the objective's `constrain` to obtain sigma. Separate +package metadata/builds and dependency selection are real setup costs. Source +hashes, wheel hashes, installed versions, module paths and test outcomes are +recorded so this narrow installation result is reproducible. A clean-room +external author experiment remains unverified. The subsequent +[P7 resident value matrix](../../benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md) +passes default quality but fails the GPU performance budget; the independent +example also has worse proper scores on that dataset/configuration. + + +## Real CUDA installation check + +From a clean committed checkout with Modal configured: + +```sh +uv run --no-sync python -m benchmarks.foundation.prepare --suite extensions +uv run --no-sync modal run benchmarks/foundation/modal_app.py::foundation_extensions +``` + +Only the three built wheels, exact test/demo files and a hashed manifest upload +into an isolated Linux T4 container; repository source is not mounted. The two +extension modules are compared byte-for-byte with the installed wheel contents. +GPU finite-difference NLL/Fisher checks precede actual two-round fits at 16 and +4097 rows, separately enabling schedule and clipping and then composing all three +extensions. CPU/CUDA raw/NLL/CRPS agreement, changed subsequent gradients, bounded +leaf values and nonconstant coefficients are checked. The public example runs as +`python extension_demo.py --device cuda` in a new process. Both extension packages +are then uninstalled and another interpreter checks exact CPU predictions for +nine GPU-trained saved models without the plugins importable. + +For an independent CUDA environment, install the extension's `cuda` extra or +OpenBoost's CUDA extra as well as both wheels. CUDA requires supported NVIDIA +hardware; CPU imports do not import CuPy. `step` uses the explicit context's +array module and rejects mixed/host arrays in CUDA calls; `constrain` accepts +same-device NumPy/CuPy raw arrays. Initialization remains on CPU. `loss_value` +returns an explicit host scalar; step/rule vector arithmetic stays on device. +The strict trainer's copies/scalar synchronization and unsupported eval/callbacks +remain as documented in the public experimental guide. No profiler trace or +speed/cost/adoption conclusion follows from this conformance suite. + + +The runnable demo uses a `main` entry guard. A standalone GPU script without +that guard failed when CUDA availability discovery spawned a Python worker and +re-entered top-level training. Keep training out of import-time execution; the +CPU installer explicitly tests importing the demo as `__mp_main__` without +creating a model. The failed real-device run is retained in foundation evidence. + + +## Independent author trial + +[AUTHOR_TASK.md](AUTHOR_TASK.md) provides an unsolved extension task and a record +for elapsed time, assistance, private imports/core changes, GPU results and +willingness to depend on the package. No outside author has completed it yet. diff --git a/examples/extensions/bounded_leaves/README.md b/examples/extensions/bounded_leaves/README.md new file mode 100644 index 0000000..0d15e04 --- /dev/null +++ b/examples/extensions/bounded_leaves/README.md @@ -0,0 +1,14 @@ +# bounded_leaves + +Independent CPU/CUDA example package: `from bounded_leaves import BoundedNewton`. +Install its wheel alongside OpenBoost 1.0.0rc1; see +[the shared installation guide](../README.md) and executable `../demo.py`. + +Pass `BoundedNewton(bound=.5)` to `LevelWiseBuilder(leaf_rule=...)`. The rule uses +public `NewtonLeafRule` and clips its outputs to ±bound on the context's array +backend. Bound must be finite and positive; version 0.2.0 declares CPU/CUDA capability. +Empty/zero-gradient nodes stay zero. The builder retains its original split +criterion. `tests/test_leaf.py` checks independent values and invalid bounds; +shared composition tests verify changed leaves, next gradients and persistence. +Install the `cuda` extra for GPU dependencies; the shared guide describes the +real-device installed-wheel verification. diff --git a/examples/extensions/bounded_leaves/pyproject.toml b/examples/extensions/bounded_leaves/pyproject.toml new file mode 100644 index 0000000..b67f4a7 --- /dev/null +++ b/examples/extensions/bounded_leaves/pyproject.toml @@ -0,0 +1,16 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "openboost-example-bounded-leaves" +version = "0.2.0" +description = "Independent OpenBoost experimental bounded_leaves example" +requires-python = ">=3.10" +dependencies = ["openboost==1.0.0rc1", "numpy>=1.24"] + +[project.optional-dependencies] +cuda = ["openboost[cuda]==1.0.0rc1"] + +[tool.hatch.build.targets.wheel] +packages = ["src/bounded_leaves"] diff --git a/examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py b/examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py new file mode 100644 index 0000000..b254317 --- /dev/null +++ b/examples/extensions/bounded_leaves/src/bounded_leaves/__init__.py @@ -0,0 +1,22 @@ +"""Independent bounded Newton rule using only the public extension API.""" + +import numpy as np + +from openboost.experimental import NewtonLeafRule + + +class BoundedNewton: + """Clip Newton leaf outputs; retain the builder's original split criterion.""" + + supported_devices = frozenset({"cpu", "cuda"}) + + def __init__(self, bound=0.5): + if not np.isfinite(bound) or bound <= 0: + raise ValueError("bound must be finite and positive") + self.bound = float(bound) + + def values(self, G, H, *, config, context): + if context.device not in self.supported_devices: + raise ValueError("Unsupported execution device") + values = NewtonLeafRule().values(G, H, config=config, context=context) + return context.xp.clip(values, -self.bound, self.bound) diff --git a/examples/extensions/bounded_leaves/tests/test_leaf.py b/examples/extensions/bounded_leaves/tests/test_leaf.py new file mode 100644 index 0000000..052f204 --- /dev/null +++ b/examples/extensions/bounded_leaves/tests/test_leaf.py @@ -0,0 +1,22 @@ +import numpy as np +import pytest +from bounded_leaves import BoundedNewton + +from openboost.experimental import ExecutionContext, TrainerConfig + + +def test_bounded_reference_and_zero_nodes(): + ctx = ExecutionContext("cpu", np, np.random.default_rng(7), 0, "mu") + G = np.array([0, 8, -6, 0], np.float32) + H = np.array([0, 1, 2, 3], np.float32) + result = BoundedNewton(0.5).values(G, H, config=TrainerConfig(), context=ctx) + np.testing.assert_array_equal(result, [0, -0.5, 0.5, 0]) + assert result.dtype == np.float32 + np.testing.assert_array_equal(G, [0, 8, -6, 0]) + for value in (0, -1, np.nan, np.inf): + with pytest.raises(ValueError): + BoundedNewton(value) + + +def test_declared_devices(): + assert BoundedNewton.supported_devices == frozenset({"cpu", "cuda"}) diff --git a/examples/extensions/check_inference.py b/examples/extensions/check_inference.py new file mode 100644 index 0000000..62c87f9 --- /dev/null +++ b/examples/extensions/check_inference.py @@ -0,0 +1,24 @@ +"""Run in a fresh interpreter after uninstalling both training extensions.""" + +import importlib.util +import json +import warnings +from pathlib import Path + +import numpy as np + +from openboost.experimental import Booster + +for name in ("normal_fisher", "bounded_leaves"): + assert importlib.util.find_spec(name) is None, name +count = 0 +for model_path in sorted(Path("saved").glob("*.ob")) + [Path("demo.ob")]: + expected = np.load(model_path.with_suffix(".npz")) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + model = Booster.load(model_path) + for channel, actual in model.predict_raw(expected["X"]).items(): + np.testing.assert_array_equal(actual, expected[channel]) + count += 1 +assert count == 6 +print(json.dumps({"extensions_absent": True, "exact_cpu_roundtrips": count})) diff --git a/examples/extensions/demo.py b/examples/extensions/demo.py new file mode 100644 index 0000000..45f423e --- /dev/null +++ b/examples/extensions/demo.py @@ -0,0 +1,52 @@ +"""Public CPU/CUDA A+B+C example, also executed by verify_wheels.py.""" + +import argparse +import hashlib +import json + +import numpy as np +from bounded_leaves import BoundedNewton +from normal_fisher import ChannelDecay, NormalFisher + +from openboost.experimental import Booster, LevelWiseBuilder, TrainerConfig + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--device", choices=("cpu", "cuda"), default="cpu") + args = parser.parse_args() + + rng = np.random.default_rng(7) + X = rng.normal(size=(64, 3)).astype(np.float32) + y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=64)).astype(np.float32) + weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), 64) + objective = NormalFisher() + model = Booster( + objective=objective, + device=args.device, + tree_builder=LevelWiseBuilder(leaf_rule=BoundedNewton(0.5)), + step_schedule=ChannelDecay(tau=1), + config=TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.2, random_state=7), + ).fit(X, y, sample_weight=weights) + params = objective.constrain(model.predict_raw(X)) + assert params["mu"].shape == (64,) and np.all(params["sigma"] > 0) + model.save("demo.ob") + np.savez("demo.npz", X=X, **model.predict_raw(X)) + print( + json.dumps( + { + "data_sha256": hashlib.sha256( + X.tobytes() + y.tobytes() + weights.tobytes() + ).hexdigest(), + "device": model.fit_report_["actual_device"], + "samples": 64, + "seed": 7, + "coefficients": model.coefficients_, + "finite_positive_scale": bool(np.isfinite(params["sigma"]).all()), + } + ) + ) + + +if __name__ == "__main__": + main() diff --git a/examples/extensions/normal_fisher/README.md b/examples/extensions/normal_fisher/README.md new file mode 100644 index 0000000..12e9e96 --- /dev/null +++ b/examples/extensions/normal_fisher/README.md @@ -0,0 +1,18 @@ +# normal_fisher + +Independent CPU/CUDA example package: `from normal_fisher import NormalFisher, +ChannelDecay`. Install its wheel alongside OpenBoost 1.0.0rc1; see +[the shared installation guide](../README.md) and executable `../demo.py`. + +`NormalFisher` uses raw `mu` and `log_sigma`, weighted NLL gradients and diagonal +expected Fisher `(exp(-2*log_sigma), 2)`, with weights applied once. Initialization +uses weighted mean and variance floored at 1e-6. `constrain` returns mu/sigma. +Invalid weights, unsupported extra targets and non-finite states fail. + +`ChannelDecay(tau=1)` returns full coefficients +`base_lr * {mu: 1, log_sigma: .5} / (1 + round_idx/tau)`. This is a predetermined +schedule, not line search. `tests/test_normal.py` supplies an independent +finite-difference NLL and analytic Fisher reference. Version 0.2.0 uses explicit NumPy/CuPy context arithmetic. CUDA input vectors +must already be on the current device; CPU import has no CuPy requirement. +Install the `cuda` extra for GPU dependencies. See the shared guide for the +real-device wheel verification command. diff --git a/examples/extensions/normal_fisher/pyproject.toml b/examples/extensions/normal_fisher/pyproject.toml new file mode 100644 index 0000000..045f926 --- /dev/null +++ b/examples/extensions/normal_fisher/pyproject.toml @@ -0,0 +1,16 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "openboost-example-normal-fisher" +version = "0.2.0" +description = "Independent OpenBoost experimental normal_fisher example" +requires-python = ">=3.10" +dependencies = ["openboost==1.0.0rc1", "numpy>=1.24"] + +[project.optional-dependencies] +cuda = ["openboost[cuda]==1.0.0rc1"] + +[tool.hatch.build.targets.wheel] +packages = ["src/normal_fisher"] diff --git a/examples/extensions/normal_fisher/src/normal_fisher/__init__.py b/examples/extensions/normal_fisher/src/normal_fisher/__init__.py new file mode 100644 index 0000000..5e6dc4e --- /dev/null +++ b/examples/extensions/normal_fisher/src/normal_fisher/__init__.py @@ -0,0 +1,122 @@ +"""Independent Gaussian/Fisher objective and predetermined channel schedule.""" + +import numpy as np + + +class NormalFisher: + """Weighted Normal NLL gradients with diagonal expected Fisher curvature. + + NumPy/CuPy arithmetic; no raw clipping or exposure support. + """ + + channel_names = ("mu", "log_sigma") + supported_devices = frozenset({"cpu", "cuda"}) + + @staticmethod + def _inputs(y, sample_weight, extra, xp=np): + if extra: + raise ValueError("NormalFisher does not support extra targets/exposure") + y = xp.asarray(y, dtype=xp.float64) + if y.ndim != 1 or not y.size or not bool(xp.isfinite(y).all()): + raise ValueError("Require nonempty finite one-dimensional targets") + w = ( + xp.ones_like(y) + if sample_weight is None + else xp.asarray(sample_weight, dtype=xp.float64) + ) + if w.shape != y.shape or not bool(xp.isfinite(w).all()) or (w < 0).any() or w.sum() <= 0: + raise ValueError("Require finite nonnegative weights with positive sum") + return y, w + + def init_raw(self, y, sample_weight=None, extra=None): + y, w = self._inputs(y, sample_weight, extra) + mu = np.average(y, weights=w) + variance = max(float(np.average((y - mu) ** 2, weights=w)), 1e-6) + return {"mu": float(mu), "log_sigma": float(0.5 * np.log(variance))} + + def _terms(self, raw, y, sample_weight, extra, context): + xp = context.xp + if context.device == "cuda": + import cupy as cp + + inputs = list(raw.values()) + [y] + ([] if sample_weight is None else [sample_weight]) + if xp is not cp or any( + not isinstance(a, cp.ndarray) or a.device.id != cp.cuda.runtime.getDevice() + for a in inputs + ): + raise ValueError("CUDA inputs must be arrays on the current device") + elif context.device != "cpu" or xp is not np: + raise ValueError("Invalid execution device") + y, w = self._inputs(y, sample_weight, extra, xp) + if set(raw) != set(self.channel_names): + raise ValueError("Require mu and log_sigma channels") + if any(v.shape != y.shape or not bool(xp.isfinite(v).all()) for v in raw.values()): + raise ValueError("Invalid raw shape or non-finite state") + residual = raw["mu"].astype(float) - y + logs = raw["log_sigma"].astype(float) + with np.errstate(over="ignore", invalid="ignore"): + precision = xp.exp(-2 * logs) + squared = residual**2 * precision + if ( + not bool(xp.isfinite(precision).all()) + or bool(xp.any(precision <= 0)) + or not bool(xp.isfinite(squared).all()) + ): + raise ValueError("Non-finite Normal state") + return residual, logs, precision, squared, w + + def step(self, raw, y, sample_weight=None, extra=None, *, context): + xp = context.xp + residual, _, precision, squared, w = self._terms(raw, y, sample_weight, extra, context) + with np.errstate(over="ignore", invalid="ignore"): + arrays = [ + xp.ascontiguousarray(v, dtype=xp.float32) + for v in (residual * precision * w, precision * w, (1 - squared) * w, 2 * w) + ] + if any(not bool(xp.isfinite(v).all()) for v in arrays): + raise ValueError("Non-finite float32 Normal statistics") + return {"mu": (arrays[0], arrays[1]), "log_sigma": (arrays[2], arrays[3])} + + def loss_value(self, raw, y, sample_weight=None, extra=None, *, context): + _, logs, _, squared, w = self._terms(raw, y, sample_weight, extra, context) + return float(context.xp.average(logs + 0.5 * squared + 0.5 * np.log(2 * np.pi), weights=w)) + + def constrain(self, raw, extra=None): + if extra or set(raw) != set(self.channel_names): + raise ValueError("Require mu/log_sigma without extra targets") + xp = np + if any(hasattr(a, "__cuda_array_interface__") for a in raw.values()): + import cupy as cp + + xp = cp + if any( + not isinstance(a, cp.ndarray) or a.device.id != cp.cuda.runtime.getDevice() + for a in raw.values() + ): + raise ValueError("Parameters must share the current device") + with np.errstate(over="ignore", invalid="ignore"): + sigma = xp.exp(raw["log_sigma"].astype(float)) + if ( + not bool(xp.isfinite(raw["mu"]).all()) + or not bool(xp.isfinite(sigma).all()) + or (sigma <= 0).any() + ): + raise ValueError("Non-finite or nonpositive Normal scale") + return {"mu": raw["mu"].copy(), "sigma": sigma} + + +class ChannelDecay: + """Full coefficients base_lr * channel_scale / (1 + round_idx / tau).""" + + def __init__(self, tau=1.0): + if not np.isfinite(tau) or tau <= 0: + raise ValueError("tau must be finite and positive") + self.tau = float(tau) + + def coefficients(self, round_idx, channel_names, base_learning_rate): + if tuple(channel_names) != ("mu", "log_sigma"): + raise ValueError("ChannelDecay requires ordered mu/log_sigma channels") + if round_idx < 0 or not np.isfinite(base_learning_rate) or base_learning_rate < 0: + raise ValueError("Invalid round or base learning rate") + rate = base_learning_rate / (1 + round_idx / self.tau) + return {"mu": rate, "log_sigma": 0.5 * rate} diff --git a/examples/extensions/normal_fisher/tests/test_normal.py b/examples/extensions/normal_fisher/tests/test_normal.py new file mode 100644 index 0000000..18515b7 --- /dev/null +++ b/examples/extensions/normal_fisher/tests/test_normal.py @@ -0,0 +1,69 @@ +import numpy as np +import pytest +from normal_fisher import ChannelDecay, NormalFisher + +from openboost.experimental import ExecutionContext + + +def ctx(): + return ExecutionContext("cpu", np, np.random.default_rng(7), 0) + + +def test_gradient_finite_difference_and_fisher(): + obj = NormalFisher() + y = np.array([-1, 2, 4], np.float32) + w = np.array([0, 0.5, 2], np.float32) + raw = { + "mu": np.array([0.2, 0.4, 0.1], np.float32), + "log_sigma": np.array([-0.3, 0.2, 0.5], np.float32), + } + stats = obj.step(raw, y, w, context=ctx()) + + def independent_nll(mu, logs): + return ( + logs + 0.5 * ((y.astype(float) - mu) * np.exp(-logs)) ** 2 + 0.5 * np.log(2 * np.pi) + ) * w + + for channel in raw: + plus = {k: v.astype(float) for k, v in raw.items()} + minus = {k: v.astype(float) for k, v in raw.items()} + plus[channel] += 1e-5 + minus[channel] -= 1e-5 + fd = ( + independent_nll(plus["mu"], plus["log_sigma"]) + - independent_nll(minus["mu"], minus["log_sigma"]) + ) / 2e-5 + np.testing.assert_allclose(stats[channel][0], fd, rtol=2e-6, atol=1e-6) + np.testing.assert_allclose(stats["mu"][1], w / np.exp(2 * raw["log_sigma"]), rtol=1e-6) + np.testing.assert_array_equal(stats["log_sigma"][1], 2 * w) + assert obj.loss_value(raw, y, w, context=ctx()) == pytest.approx( + independent_nll(raw["mu"].astype(float), raw["log_sigma"].astype(float)).sum() / w.sum() + ) + base = obj.init_raw(y, w) + mean = np.average(y.astype(float), weights=w) + assert base["mu"] == pytest.approx(mean) + assert base["log_sigma"] == pytest.approx(0.5 * np.log(np.average((y - mean) ** 2, weights=w))) + + +def test_schedule_and_invalid_inputs(): + schedule = ChannelDecay(tau=1) + assert schedule.coefficients(0, ("mu", "log_sigma"), 0.2) == {"mu": 0.2, "log_sigma": 0.1} + assert schedule.coefficients(1, ("mu", "log_sigma"), 0.2) == {"mu": 0.1, "log_sigma": 0.05} + for value in (0, -1, np.inf, np.nan): + with pytest.raises(ValueError): + ChannelDecay(tau=value) + obj = NormalFisher() + for weights in (np.zeros(2), np.array([-1, 2]), np.array([1, np.nan])): + with pytest.raises(ValueError): + obj.init_raw(np.ones(2), weights) + with pytest.raises(ValueError): + obj.init_raw(np.ones(2), extra={"exposure": np.ones(2)}) + with pytest.raises(ValueError): + obj.step({"mu": np.ones(2), "log_sigma": np.full(2, -1000)}, np.ones(2), context=ctx()) + with pytest.raises(ValueError): + obj.step({"mu": np.ones(2), "log_sigma": np.full(2, 1000)}, np.ones(2), context=ctx()) + assert obj.init_raw(np.ones(2))["log_sigma"] == pytest.approx(0.5 * np.log(1e-6)) + + +def test_declared_devices(): + assert NormalFisher.supported_devices == frozenset({"cpu", "cuda"}) diff --git a/examples/extensions/requirements-cpu.txt b/examples/extensions/requirements-cpu.txt new file mode 100644 index 0000000..49d0b80 --- /dev/null +++ b/examples/extensions/requirements-cpu.txt @@ -0,0 +1,10 @@ +numpy==2.2.6 +numba==0.61.2 +llvmlite==0.44.0 +scipy==1.15.3 +joblib==1.5.3 +pytest==9.0.2 +iniconfig==2.3.0 +packaging==25.0 +pluggy==1.6.0 +pygments==2.19.2 diff --git a/examples/extensions/test_composition.py b/examples/extensions/test_composition.py new file mode 100644 index 0000000..2fd5392 --- /dev/null +++ b/examples/extensions/test_composition.py @@ -0,0 +1,127 @@ +"""Installed-wheel composition: public imports only, no repository helpers.""" + +import json +from pathlib import Path + +import numpy as np +import pytest +from bounded_leaves import BoundedNewton +from normal_fisher import ChannelDecay, NormalFisher + +from openboost.experimental import Booster, LevelWiseBuilder, TrainerConfig + + +def fit(rule=None, schedule=None, **kwargs): + X = np.arange(4, dtype=np.float32)[:, None] + y = np.array([-3, -1, 1, 3], np.float32) + model = Booster( + objective=NormalFisher(), + tree_builder=LevelWiseBuilder(leaf_rule=rule), + step_schedule=schedule, + config=TrainerConfig( + n_trees=2, + max_depth=1, + learning_rate=0.5, + reg_lambda=1, + min_child_weight=0, + random_state=7, + ), + ).fit(X, y, **kwargs) + return X, y, model + + +def test_two_round_reference_and_composition(): + X, y, combined = fit(BoundedNewton(0.1), ChannelDecay()) + _, _, default = fit() + _, _, scheduled = fit(schedule=ChannelDecay()) + _, _, bounded = fit(rule=BoundedNewton(0.1)) + assert combined.coefficients_ == {"mu": [0.5, 0.25], "log_sigma": [0.25, 0.125]} + assert all(v == [0.5, 0.5] for v in default.coefficients_.values()) + # Independent exhaustive depth-one reference on the original feature rows. + raw = {"mu": np.zeros(4, np.float32), "log_sigma": np.full(4, 0.5 * np.log(5), np.float32)} + second_grad = {} + for r in range(2): + residual = raw["mu"].astype(float) - y + precision = np.exp(-2 * raw["log_sigma"].astype(float)) + stats = { + "mu": (residual * precision, precision), + "log_sigma": (1 - residual**2 * precision, np.full(4, 2.0)), + } + if r == 1: + second_grad = {k: v[0].copy() for k, v in stats.items()} + for channel, (g, h) in stats.items(): + # Production statistics are float32; emulate that public boundary only. + g, h = g.astype(np.float32).astype(float), h.astype(np.float32).astype(float) + scores = [] + for split in range(1, 4): + score = ( + g[:split].sum() ** 2 / (h[:split].sum() + 1) + + g[split:].sum() ** 2 / (h[split:].sum() + 1) + - g.sum() ** 2 / (h.sum() + 1) + ) + scores.append(score) + split = int(np.argmax(scores)) + 1 + groups = [np.arange(split), np.arange(split, 4)] if max(scores) > 0 else [np.arange(4)] + pred = np.zeros(4, np.float32) + for rows in groups: + pred[rows] = np.clip(-g[rows].sum() / (h[rows].sum() + 1), -0.1, 0.1) + np.testing.assert_allclose( + combined.trees_[channel][r](combined.X_binned_), pred, atol=1e-7 + ) + raw[channel] += ([0.5, 0.25] if channel == "mu" else [0.25, 0.125])[r] * pred + for k in raw: + np.testing.assert_allclose(combined.predict_raw(X)[k], raw[k], atol=1e-7) + assert not np.allclose(default.predict_raw(X)["mu"], scheduled.predict_raw(X)["mu"]) + assert not np.allclose(default.predict_raw(X)["mu"], bounded.predict_raw(X)["mu"]) + # Reconstruct unbounded first round through public tree/coefficient state. + initial = NormalFisher().init_raw(y) + first = { + k: initial[k] + scheduled.coefficients_[k][0] * scheduled.trees_[k][0](scheduled.X_binned_) + for k in raw + } + unbounded_next_mu = (first["mu"] - y) * np.exp(-2 * first["log_sigma"]) + assert not np.allclose(second_grad["mu"], unbounded_next_mu) + out = Path("saved") + out.mkdir(exist_ok=True) + for name, model in [ + ("combined", combined), + ("default", default), + ("scheduled", scheduled), + ("bounded", bounded), + ]: + model.save(out / (name + ".ob")) + np.savez(out / (name + ".npz"), X=X, **model.predict_raw(X)) + + +def test_early_stopping_and_roundtrip(): + X = np.arange(4, dtype=np.float32)[:, None] + y = np.array([-3, -1, 1, 3], np.float32) + model = Booster( + objective=NormalFisher(), + tree_builder=LevelWiseBuilder(), + step_schedule=ChannelDecay(), + config=TrainerConfig(n_trees=10, max_depth=1, learning_rate=0.5, min_child_weight=0), + ).fit(X, y, eval_sets=[{"X": X, "y": -y}], early_stopping_rounds=1) + assert model.best_iteration_ == 0 + assert model.coefficients_ == {"mu": [0.5], "log_sigma": [0.25]} + assert all(len(t) == 1 for t in model.trees_.values()) + out = Path("saved") + out.mkdir(exist_ok=True) + model.save(out / "early.ob") + np.savez(out / "early.npz", X=X, **model.predict_raw(X)) + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(out / "early.ob") + for k, v in model.predict_raw(X).items(): + np.testing.assert_array_equal(loaded.predict_raw(X)[k], v) + Path("composition.json").write_text( + json.dumps( + { + "models": 5, + "early_best_iteration": model.best_iteration_, + "channels": ["mu", "log_sigma"], + "seed": 7, + "samples": 4, + "two_round_oracle": True, + } + ) + ) diff --git a/examples/extensions/verify_wheels.py b/examples/extensions/verify_wheels.py new file mode 100644 index 0000000..b4b2289 --- /dev/null +++ b/examples/extensions/verify_wheels.py @@ -0,0 +1,184 @@ +"""Build and verify three wheels in a fresh, non-editable, outside-repo venv.""" + +import ast +import hashlib +import json +import os +import platform +import shutil +import subprocess +import sys +import tempfile +import xml.etree.ElementTree as ET +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +HERE = Path(__file__).resolve().parent + + +def run(args, cwd, env): + try: + return subprocess.check_output(args, cwd=cwd, env=env, text=True, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as exc: + print(exc.output, flush=True) + raise + + +def verify(output): + env = dict( + os.environ, + OPENBOOST_BACKEND="cpu", + NUMBA_NUM_THREADS="1", + OMP_NUM_THREADS="1", + OPENBLAS_NUM_THREADS="1", + ) + env.pop("PYTHONPATH", None) + env.pop("VIRTUAL_ENV", None) + source_files = ( + list(HERE.rglob("*.py")) + + list(HERE.rglob("pyproject.toml")) + + list(HERE.rglob("README.md")) + + [HERE / "requirements-cpu.txt"] + ) + source_files = [p for p in source_files if "__pycache__" not in p.parts] + public_imports = set() + for p in HERE.glob("*/src/**/*.py"): + for node in ast.walk(ast.parse(p.read_text())): + names = ( + [node.module or ""] + if isinstance(node, ast.ImportFrom) + else [n.name for n in node.names] + if isinstance(node, ast.Import) + else [] + ) + for name in names: + if name.startswith("openboost"): + assert name in {"openboost", "openboost.experimental"}, (p, name) + public_imports.add(name) + with tempfile.TemporaryDirectory(prefix="openboost-extension-") as temp: + work = Path(temp) + wheels = work / "wheels" + wheels.mkdir() + for project in [ROOT, HERE / "normal_fisher", HERE / "bounded_leaves"]: + run(["uv", "build", "--wheel", "--out-dir", str(wheels), str(project)], work, env) + venv = work / "venv" + run(["uv", "venv", "--python", sys.executable, str(venv)], work, env) + python = str(venv / "bin/python") + run( + [ + "uv", + "pip", + "install", + "--python", + python, + *map(str, sorted(wheels.glob("*.whl"))), + "-r", + str(HERE / "requirements-cpu.txt"), + ], + work, + env, + ) + frozen = json.loads( + run( + [ + python, + "-c", + "import json,importlib.metadata as m; print(json.dumps(sorted(d.metadata['Name']+'=='+d.version for d in m.distributions())))", + ], + work, + env, + ) + ) + installed = json.loads( + run( + [ + python, + "-c", + "import json,sys,openboost,normal_fisher,bounded_leaves; from pathlib import Path; print(json.dumps({m.__name__:str(Path(m.__file__).relative_to(sys.prefix)) for m in (openboost,normal_fisher,bounded_leaves)}))", + ], + work, + env, + ) + ) + assert all("site-packages/" in p for p in installed.values()) + shutil.copy(HERE / "normal_fisher/tests/test_normal.py", work / "test_normal.py") + shutil.copy(HERE / "bounded_leaves/tests/test_leaf.py", work / "test_leaf.py") + shutil.copy(HERE / "test_composition.py", work / "test_composition.py") + shutil.copy(HERE / "check_inference.py", work / "check_inference.py") + shutil.copy(HERE / "demo.py", work / "demo.py") + # Spawned CUDA discovery workers re-import __main__; importing the demo + # must not parse arguments, train or write model files. + run([python, "-c", "import runpy; from pathlib import Path; runpy.run_path('demo.py', run_name='__mp_main__'); assert not Path('demo.ob').exists()"], work, env) + demo = json.loads(run([python, "demo.py"], work, env)) + test_output = run( + [ + python, + "-m", + "pytest", + "-q", + "--junitxml=junit.xml", + "test_normal.py", + "test_leaf.py", + "test_composition.py", + ], + work, + env, + ) + run( + [ + "uv", + "pip", + "uninstall", + "--python", + python, + "openboost-example-normal-fisher", + "openboost-example-bounded-leaves", + ], + work, + env, + ) + inference = json.loads(run([python, "check_inference.py"], work, env)) + result = { + "source_sha": run(["git", "rev-parse", "HEAD"], ROOT, env).strip(), + "source_dirty": bool(run(["git", "status", "--porcelain"], ROOT, env).strip()), + "files": { + str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in sorted(source_files) + }, + "wheel_hashes": { + p.name: hashlib.sha256(p.read_bytes()).hexdigest() + for p in sorted(wheels.glob("*.whl")) + }, + "uv_lock_sha256": hashlib.sha256((ROOT / "uv.lock").read_bytes()).hexdigest(), + "python": platform.python_version(), + "os": platform.system(), + "machine": platform.machine(), + "threads": 1, + "uv_version": run(["uv", "--version"], work, env).strip(), + "packages": frozen, + "module_paths_relative_to_venv": installed, + "extension_openboost_imports": sorted(public_imports), + "method_source_lines": { + p.parent.name: len(p.read_text().splitlines()) + for p in HERE.glob("*/src/*/__init__.py") + }, + "inference_after_uninstall": inference, + "demo": demo, + "composition": json.loads((work / "composition.json").read_text()), + "command": "uv run --no-sync python examples/extensions/verify_wheels.py OUTPUT", + "scope": "CPU installation and mathematical conformance; no external adoption or GPU claim", + } + # Only sanitized evidence leaves the disposable environment. + output.mkdir(parents=True, exist_ok=True) + (output / "results.json").write_text(json.dumps(result, indent=2) + "\n") + junit = ET.fromstring((work / "junit.xml").read_text()) + for suite in junit.iter("testsuite"): + suite.attrib.pop("hostname", None) + ET.ElementTree(junit).write(output / "junit.xml", encoding="unicode", xml_declaration=True) + print(test_output.replace(str(work), "")) + print(json.dumps(inference)) + print("Evidence:", output) + + +if __name__ == "__main__": + verify(Path(sys.argv[1]).resolve()) diff --git a/examples/gpu_training.py b/examples/gpu_training.py index 949d32a..622318a 100644 --- a/examples/gpu_training.py +++ b/examples/gpu_training.py @@ -226,23 +226,17 @@ def main(): """ print(best_practices) - # --- Scaling Guide --- - print("\n8. Expected GPU speedups by dataset size...") + # --- Benchmark Evidence --- + print("\n8. How to measure GPU value...") scaling_info = """ - | Dataset Size | Features | Trees | Expected Speedup | - |--------------|----------|-------|------------------| - | 5K samples | 10 | 100 | ~1-2x | - | 10K samples | 20 | 100 | ~2-5x | - | 50K samples | 20 | 100 | ~3-7x | - | 100K samples | 50 | 200 | ~5-10x | - | 500K samples | 100 | 500 | ~10-20x | - - Factors affecting speedup: - - More features = better GPU utilization - - More bins = better GPU utilization - - GAM shows best speedups (parallel feature updates) - - First run includes JIT compilation overhead + GPU speedup is workload- and hardware-dependent. For a publishable result: + - Force CPU and CUDA backends explicitly + - Verify prediction and task-metric parity first + - State whether JIT warm-up is excluded + - Run repeated fit and predict timings + - Record peak memory, hardware, CUDA/driver, seeds, and failures + - Store raw results with the exact OpenBoost commit """ print(scaling_info) @@ -250,8 +244,7 @@ def main(): print("\n9. Multi-GPU training...") print(""" - For datasets that don't fit on a single GPU or to speed up training further, - OpenBoost supports multi-GPU training via Ray: + OpenBoost includes an experimental multi-GPU path via Ray: # Install Ray pip install ray[default] @@ -273,7 +266,10 @@ def main(): Multi-GPU training uses data parallelism: - Each GPU processes a subset of samples - Histograms are aggregated across GPUs - - Near-linear scaling with number of GPUs + - sample_weight is not supported + + Do not infer scaling from this example. The repository still needs a + checked-in two-/four-GPU parity and repeated-timing artifact. """) # --- Summary --- diff --git a/examples/v1_extensions/README.md b/examples/v1_extensions/README.md new file mode 100644 index 0000000..1124a56 --- /dev/null +++ b/examples/v1_extensions/README.md @@ -0,0 +1,72 @@ +# Current v1 development extension wheels + +Four repository-authored packages exercise the installed public CPU foundation: + +- `ob-expectile`: D1 composes weighted expectile geometry and initialization with + public Newton trees, transactions and stopping. See [semantics](expectile/README.md). +- `ob-cohort-splits`: D2 adds cohort information independent of training weights + and rejects candidates unless each child has at least one unit per cohort. + Cohorts are not input features. Existing histograms, growth and scoring are reused. +- `ob-penalized-leaves`: D3 replaces routed leaf solving with an independent + bisection implementation of weighted pinball plus an anchored quadratic penalty. + The built-in leaf solver uses a different breakpoint algorithm. +- `ob-ordered-updates`: D4 recomputes Normal/Formula geometry after each accepted + parameter and applies bounded backtracking through public transactions. See + [ordered update semantics and limits](ordered_updates/README.md). + +These are exploratory development examples, not independent authors, timed agent +comparisons, held-out tasks or adoption evidence. All use public imports and +require no core edits. The historical `examples/extensions/` packages remain separate. + +## Reproduce + +```sh +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python examples/v1_extensions/verify.py /tmp/openboost-extension-check +``` + +The verifier builds five wheels offline, creates a fresh environment, installs +the wheels and NumPy 2.3.5, and executes copied checks from outside the repository +using Python `-I`. Installed module paths must be under site-packages. It then +uninstalls all four extensions and starts another interpreter to load nine saved +models and verify exact predictions with none of the extensions importable. +The report records source/wheel hashes, revision/dirty state, environment, +commands, three-round outputs and failures. Offline dependencies must already be +cached. Nothing is uploaded or published. Run with Python 3.12 for the recorded +environment; broader platform support has not been checked. + +## Public composition + +Pass `CohortLearner(problem, information)` as `squared(..., learner=...)`. +Information has shape `[N, cohorts]` and is bound to that problem's identity. +The learner owns tree settings; the recipe rejects conflicting growth settings. + +Pass `PenalizedLeaves(q=0.7, penalty=5, anchor=4)` as +`quantile(..., q=0.7, grower=...)`. Keep the recipe and solver quantile aligned +for the D3 task. The plugin owns leaf penalty/anchor and replaces the default +row_leaf callback; recipe penalty/anchor options should remain at their defaults. +The scoring and prediction loss remain unpenalized pinball as declared in D3. + +This highlights a usability cost: leaf replacement currently uses a grower +adapter, and the quantile value is supplied in two places. Neither requires loop +copying or private access. Record this friction for later author trials before +adding another core configuration abstraction. + +## Evidence and limits + +`checks.py` independently enumerates D2 feasible cuts, checks the unconstrained +optimum is rejected, tests all three growth policies and a no-feasible split, +and verifies information survives zero objective weights. D3 uses exhaustive +breakpoint/stationary-point minimization, subgradient containment and penalty +contraction over deterministic weighted fixtures, including duplicate residuals +and zero weights. Three-round execution verifies changed subsequent raw values +and each externally solved leaf against the oracle. Mixed/missing feature models +round-trip after plugin removal. + +Source development tests in `tests/v1/test_public_extensions.py` are distinct +from the installed-wheel checks. These results are partial E2/E6 evidence; +Complete D5 author evaluation, formal E5, CUDA, real-data quality/cost +and external adoption remain open. Structural result integration now permits +ordered recipes in run_many; installed M=1/8/32 checks exercise mixed recipes, +independent stopping, failure isolation and reorder/regroup/retry equivalence. +D4 reference traces and installed results are +recorded separately from the earlier D2/D3 evidence. diff --git a/examples/v1_extensions/checks.py b/examples/v1_extensions/checks.py new file mode 100644 index 0000000..c3199dc --- /dev/null +++ b/examples/v1_extensions/checks.py @@ -0,0 +1,186 @@ +"""Independent development oracles; also executable against installed wheels.""" + +import json +from dataclasses import replace +from pathlib import Path + +import numpy as np + +from openboost import MixedData, NumericData, Problem, RunContext +from openboost.binning import Binning +from openboost.leaves import ResidualView +from openboost.recipes import quantile, squared +from openboost.stats import newton +from openboost.tree import best_first, depthwise, symmetric + + +def optimum(residual, weight, q, penalty, anchor): + """Enumerate every breakpoint and each interval's stationary candidate.""" + candidates = list(residual) + edges = [-np.inf, *sorted(set(residual)), np.inf] + for low, high in zip(edges[:-1], edges[1:], strict=True): + mass_left = weight[residual <= low].sum() + root = anchor + (q * weight.sum() - mass_left) / penalty + if low <= root <= high: + candidates.append(root) + + def loss(value): + r = residual - value + return np.dot(weight, np.maximum(q * r, (q - 1) * r)) + penalty * (value - anchor) ** 2 / 2 + + return min(candidates, key=loss) + + +def run_checks(cohort, leaves, destination): + destination = Path(destination) + destination.mkdir(parents=True, exist_ok=True) + x = NumericData(np.arange(6)[:, None], np.arange(6), ("x",)) + p = Problem(x, np.zeros((6, 1)), x.row_ids) + gradient = np.array([-6.0, 1, 1, 1, 1, 2]) + information = np.eye(2)[np.arange(6) % 2] + binned = Binning.fit(x, bins=6).transform(x) + choices = [] + for cut in range(5): + left = binned.codes[0] <= cut + right = ~left + if np.all(information[left].sum(0) >= 1) and np.all(information[right].sum(0) >= 1): + gain = 0.5 * ( + gradient[left].sum() ** 2 / (left.sum() + 1) + + gradient[right].sum() ** 2 / (right.sum() + 1) + - gradient.sum() ** 2 / 7 + ) + choices.append((gain, -cut)) + expected_cut = -max(choices)[1] + cuts = [] + for grower in (depthwise, best_first, symmetric): + learner = cohort.CohortLearner(p, information, grower=grower, max_depth=1) + tree = learner(binned, newton(p, gradient, np.ones(6))) + assert tree.threshold[0] == expected_cut + assert ( + depthwise(binned, newton(p, gradient, np.ones(6)), max_depth=1).threshold[0] + != expected_cut + ) + cuts.append(int(tree.threshold[0])) + # Information survives zero objective weights; it is never reweighted. + weighted = replace(p, weight=[0, 1, 1, 1, 1, 1]) + learner = cohort.CohortLearner(weighted, information, max_depth=1) + captured = [] + + def capture(data, fields, **options): + captured.append(fields) + return depthwise(data, fields, **options) + + learner.grower = capture + learner(binned, newton(weighted, gradient, np.ones(6))) + np.testing.assert_array_equal(captured[0].values[:, -2:], information) + try: + learner(binned, newton(p, gradient, np.ones(6))) + except ValueError: + pass + else: + raise AssertionError("foreign problem accepted") + grouped = NumericData([[0], [0], [0], [1], [1], [1]], np.arange(6), ("x",)) + impossible = Problem(grouped, p.target, grouped.row_ids) + no_split = cohort.CohortLearner(impossible, np.eye(2)[[0, 0, 0, 1, 1, 1]])( + Binning.fit(grouped, bins=2).transform(grouped), newton(impossible, gradient, np.ones(6)) + ) + assert no_split.feature.tolist() == [-1] + + rng = np.random.default_rng(123) + errors = [] + for i in range(30): + residual = rng.integers(-5, 6, 9).astype(float) + weight = rng.integers(0, 5, 9).astype(float) + q, penalty, anchor = (0.2, 0.5, 0.8)[i % 3], (0.3, 2.0, 20.0)[i % 3], 1.3 + view = ResidualView(np.arange(9), residual, weight) + actual = leaves.PenalizedLeaves(q=q, penalty=penalty, anchor=anchor).solve(view) + expected = optimum(residual, weight, q, penalty, anchor) + np.testing.assert_allclose(actual, expected, atol=1e-10, rtol=1e-10) + smooth = penalty * (actual - anchor) - q * weight.sum() + assert smooth + weight[residual < actual - 1e-9].sum() <= 1e-8 + assert smooth + weight[residual <= actual + 1e-9].sum() >= -1e-8 + stronger = leaves.PenalizedLeaves(q=q, penalty=penalty * 10, anchor=anchor).solve(view) + assert abs(stronger - anchor) <= abs(actual - anchor) + 1e-10 + errors.append(abs(actual - expected)) + empty = ResidualView([1], [3.0], [0.0]) + assert leaves.PenalizedLeaves(anchor=2).solve(empty) == 2 + for penalty in [0, -1, np.nan]: + try: + leaves.PenalizedLeaves(penalty=penalty) + except ValueError: + pass + else: + raise AssertionError("invalid penalty accepted") + + mixed = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [None, "b"], [5, "a"]], + np.arange(6), + ("x", "category"), + ("numeric", "categorical"), + ) + train = Problem( + mixed, [[-3], [-1], [0], [2], [4], [7]], mixed.row_ids, weight=[1, 0, 2, 1, 3, 1] + ) + d2 = squared( + train, + train, + context=RunContext("d2", 7), + rounds=3, + learner=cohort.CohortLearner(train, information), + ) + plugin = leaves.PenalizedLeaves(q=0.7, penalty=5, anchor=4) + d3 = quantile( + train, train, context=RunContext("d3", 7), rounds=3, q=0.7, max_depth=2, grower=plugin + ) + plain = quantile(train, train, context=RunContext("d3", 7), rounds=3, q=0.7, max_depth=2) + assert not np.allclose(d3.steps[1].raw_before, plain.steps[1].raw_before) + for step, term in zip(d3.steps, d3.state.model.terms, strict=True): + residual = train.target[:, 0] - step.raw_before[:, 0] + tree = term.learner + assert len(tree.value) > 1 + codes = tree.binning.transform(mixed) + pending = [(0, np.arange(len(residual)))] + while pending: + node, rows = pending.pop() + feature = tree.feature[node] + if feature == -1: + expected = optimum(residual[rows], train.weight[rows], 0.7, 5, 4) + np.testing.assert_allclose(tree.value[node, 0], expected, atol=1e-10) + else: + left = np.where( + codes.missing[feature, rows], + tree.missing_left[node], + codes.codes[feature, rows] == tree.threshold[node] + if tree.binning.categories[feature] is not None + else codes.codes[feature, rows] <= tree.threshold[node], + ) + pending.extend(((tree.left[node], rows[left]), (tree.right[node], rows[~left]))) + records = {} + for name, fit in (("d2", d2), ("d3", d3)): + fit.state.model.save(destination / f"{name}.json") + records[name] = fit.state.model.predict(mixed).tolist() + payload = dict( + values=mixed.values.tolist(), + row_ids=mixed.row_ids.tolist(), + names=list(mixed.feature_names), + kinds=list(mixed.feature_kinds), + predictions=records, + d2_cuts=cuts, + d3_max_absolute_error=max(errors), + rounds=3, + ) + (destination / "checks.json").write_text(json.dumps(payload, allow_nan=False, indent=2) + "\n") + return payload + + +if __name__ == "__main__": + import sys + + import ob_cohort_splits + import ob_penalized_leaves + + import openboost + + for module in (openboost, ob_cohort_splits, ob_penalized_leaves): + assert "site-packages" in module.__file__, module.__file__ + run_checks(ob_cohort_splits, ob_penalized_leaves, sys.argv[1]) diff --git a/examples/v1_extensions/cohort_splits/pyproject.toml b/examples/v1_extensions/cohort_splits/pyproject.toml new file mode 100644 index 0000000..e4b1cf6 --- /dev/null +++ b/examples/v1_extensions/cohort_splits/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "ob-cohort-splits" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = ["openboost==1.0.0.dev0", "numpy>=1.24"] + +[tool.hatch.build.targets.wheel] +packages = ["src/ob_cohort_splits"] diff --git a/examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py b/examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py new file mode 100644 index 0000000..ee6bda8 --- /dev/null +++ b/examples/v1_extensions/cohort_splits/src/ob_cohort_splits/__init__.py @@ -0,0 +1,56 @@ +"""D2: independent cohort information constrains ordinary Newton splits.""" + +from functools import partial + +import numpy as np + +from openboost.ops import feasible, newton_leaf, score +from openboost.tree import depthwise + + +class CohortLearner: + """Bind information columns to a problem, without adding feature columns. + + Each child needs at least one unit from every information column. Information + is independent of objective weights, which have already been applied to G/H. + """ + + def __init__(self, problem, information, *, grower=depthwise, max_depth=2, reg_lambda=1.0): + values = np.asarray(information, dtype=float) + if ( + values.ndim != 2 + or values.shape[0] != len(problem.target) + or values.shape[1] == 0 + or not np.all(np.isfinite(values)) + or np.any(values < 0) + ): + raise ValueError("aligned finite nonnegative cohort information required") + self.problem_identity = problem.identity + self.information = np.frombuffer(values.tobytes(), dtype=float).reshape(values.shape) + self.names = tuple(f"cohort:{i}" for i in range(values.shape[1])) + self.grower, self.max_depth, self.reg_lambda = grower, max_depth, reg_lambda + + def legal(self, candidate): + if not feasible(candidate): + return False + for name in self.names: + i = candidate.names.index(name) + if candidate.roles[i] != "independent": + raise ValueError("cohort information must be independent of training weights") + if candidate.left[i] < 1 or candidate.right[i] < 1: + return False + return True + + def __call__(self, binned, fields): + if fields.problem_identity != self.problem_identity: + raise ValueError("cohort information belongs to a different problem") + for i, name in enumerate(self.names): + fields = fields.add_independent(name, self.information[:, i]) + return self.grower( + binned, + fields, + max_depth=self.max_depth, + legality=self.legal, + scoring=partial(score, reg_lambda=self.reg_lambda), + leaf=partial(newton_leaf, reg_lambda=self.reg_lambda), + ) diff --git a/examples/v1_extensions/core_inference.py b/examples/v1_extensions/core_inference.py new file mode 100644 index 0000000..21f7dcb --- /dev/null +++ b/examples/v1_extensions/core_inference.py @@ -0,0 +1,31 @@ +"""Fresh-process inference after all extension distributions are uninstalled.""" + +import importlib.util +import json +import sys +from pathlib import Path + +import numpy as np + +from openboost import MixedData, NumericData +from openboost.artifacts import Model + +for name in ("ob_cohort_splits", "ob_penalized_leaves", "ob_ordered_updates", "ob_expectile"): + assert importlib.util.find_spec(name) is None +root = Path(sys.argv[1]) +record = json.loads((root / "checks.json").read_text()) +data = MixedData( + record["values"], record["row_ids"], tuple(record["names"]), tuple(record["kinds"]) +) +for name, expected in record["predictions"].items(): + np.testing.assert_array_equal(Model.load(root / f"{name}.json").predict(data), expected) +ordered = json.loads((root / "ordered-checks.json").read_text()) +numeric = NumericData(ordered["values"], np.arange(6), ("feature",)) +for name, expected in ordered["predictions"].items(): + np.testing.assert_array_equal(Model.load(root / f"{name}.json").predict(numeric), expected) +expectile = json.loads((root / "expectile-checks.json").read_text()) +numeric = NumericData(expectile["values"], np.arange(6), ("feature",)) +np.testing.assert_array_equal( + Model.load(root / "expectile-model.json").predict(numeric), expectile["raw"] +) +print("Nine models preserve exact predictions without extension imports.") diff --git a/examples/v1_extensions/expectile/README.md b/examples/v1_extensions/expectile/README.md new file mode 100644 index 0000000..6466daf --- /dev/null +++ b/examples/v1_extensions/expectile/README.md @@ -0,0 +1,36 @@ +# External expectile objective (D1 development) + +`ob-expectile` defines loss `abs(tau - I[y-F<0]) * (y-F)^2`, default tau=0.8. +`Expectile.geometry(problem, raw)` returns mean weighted loss and **unweighted** +analytic gradient/curvature. Public `newton` applies training weights once. +At zero residual the gradient is zero and curvature is `2*tau`; finite-difference +second derivatives across that kink are not the declared curvature convention. +Initialization bisects the weighted derivative of target minus offset. Zero-weight +rows do not determine its bracket. Unsupported problem structures fail explicitly. + +```python +from ob_expectile import fit +result = fit(train, validation, context=context, tau=0.8, rounds=2) +result.state.model.save("expectile.json") +``` + +The recipe composes public binning, Newton statistics, depth-two growth, +transactions and StopState. Its explicit signature accepts preparation, learning +rate, round budget and independent validation stopping; other options fail. +Fixed finite steps commit without line search. The resulting core raw model needs +no training plugin to load or predict. Add row offsets separately when converting +raw values to final expectile predictions. Best-model selection is separate from +the final model used in the development trace. + +The objective and small outer loop are authored here; no core code or private +imports are required. This duplicates loop wiring for statistics, transactions +and stopping, not tree construction. It is a deliberate measurement of current +public composition, not justification for adding a generic trainer yet. + +Run the parent `verify.py` for an isolated wheel check. Independent references use +stationary-interval base enumeration and exhaustive numeric tree growth; two rounds +cover missing values, zero weights and nonzero offsets. A fresh process loads the +raw model after all training plugins are uninstalled. These internal development +checks do not measure independent author cost, GPU or real-data quality. D1 is an +incumbent-friendly control: built-in expectile or custom-objective hooks remain +valid comparator approaches. Formal E2/E5/E6 gates remain open. diff --git a/examples/v1_extensions/expectile/pyproject.toml b/examples/v1_extensions/expectile/pyproject.toml new file mode 100644 index 0000000..dd4a599 --- /dev/null +++ b/examples/v1_extensions/expectile/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "ob-expectile" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = ["openboost==1.0.0.dev0", "numpy>=1.24"] + +[tool.hatch.build.targets.wheel] +packages = ["src/ob_expectile"] diff --git a/examples/v1_extensions/expectile/src/ob_expectile/__init__.py b/examples/v1_extensions/expectile/src/ob_expectile/__init__.py new file mode 100644 index 0000000..24e5814 --- /dev/null +++ b/examples/v1_extensions/expectile/src/ob_expectile/__init__.py @@ -0,0 +1,113 @@ +"""External D1 expectile objective and a public-operation CPU recipe.""" + +from dataclasses import dataclass +from numbers import Real + +import numpy as np + +from openboost.artifacts import TreeTerm +from openboost.binning import prepare_training +from openboost.objectives import Squared +from openboost.runtime import initialize, propose_terms, resolve +from openboost.stats import newton +from openboost.stopping import StopState +from openboost.tree import depthwise + + +@dataclass(frozen=True) +class Expectile: + tau: float = 0.8 + + def __post_init__(self): + if isinstance(self.tau, bool) or not isinstance(self.tau, Real) or not 0 < self.tau < 1: + raise ValueError("tau must be strictly between zero and one") + + def geometry(self, problem, raw): + Squared.validate(problem) + with np.errstate(over="raise", invalid="raise"): + residual = (problem.target - problem.with_offset(raw))[:, 0] + asymmetry = np.where(residual < 0, 1 - self.tau, self.tau) + loss = float(np.dot(problem.weight / problem.weight.sum(), asymmetry * residual**2)) + gradient = -2 * asymmetry * residual + curvature = 2 * asymmetry + if not np.isfinite(loss) or not np.isfinite(gradient).all(): + raise ValueError("nonfinite expectile geometry") + return loss, gradient, curvature + + def loss(self, problem, raw): + return self.geometry(problem, raw)[0] + + def base(self, problem): + """Bisection of the weighted derivative, unlike the interval oracle.""" + Squared.validate(problem) + with np.errstate(over="raise", invalid="raise"): + target = (problem.target - problem.offset)[:, 0] + active = problem.weight > 0 + y = target[active] + weight = problem.weight[active] / problem.weight.sum() + low, high = float(y.min()), float(y.max()) + for _ in range(100): + middle = low / 2 + high / 2 + residual = y - middle + derivative = np.dot( + weight, np.where(residual < 0, 1 - self.tau, self.tau) * residual + ) + if derivative > 0: + low = middle + else: + high = middle + base = np.array([low / 2 + high / 2]) + self.loss(problem, np.broadcast_to(base, (len(target), 1))) + return base + + +@dataclass(frozen=True) +class Result: + state: object + steps: tuple + stop: StopState + + +def fit( + train, + validation, + *, + context, + tau=0.8, + rounds=2, + learning_rate=0.1, + bins=254, + prepared=None, + patience=None, + min_delta=0.0, +): + """Fixed depth-two Newton updates; unsupported options raise TypeError. + + Derivatives are unweighted; newton applies training weights once. Raw models + exclude offsets, which callers must supply separately for final predictions. + """ + objective = Expectile(tau) + Squared.validate(train) + Squared.validate(validation) + if ( + isinstance(learning_rate, bool) + or not isinstance(learning_rate, Real) + or not np.isfinite(learning_rate) + or learning_rate <= 0 + ): + raise ValueError("learning_rate must be positive and finite") + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, objective.base(train), score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state + _, g, h = objective.geometry(train, state.train_raw) + tree = depthwise(binned, newton(train, g, h)) + proposal = propose_terms(state, (TreeTerm(tree, [[1]], learning_rate),)) + state = resolve(state, proposal, accept=True, score=objective.loss) + steps.append((before.train_raw, state.train_raw)) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return Result(state, tuple(steps), stop) diff --git a/examples/v1_extensions/expectile_checks.py b/examples/v1_extensions/expectile_checks.py new file mode 100644 index 0000000..c2ca872 --- /dev/null +++ b/examples/v1_extensions/expectile_checks.py @@ -0,0 +1,53 @@ +"""Copied outside the repository and executed against installed D1 wheel.""" + +import json +import sys +from pathlib import Path + +import numpy as np + +from openboost import NumericData, Problem, RunContext +from openboost.runs import RunSpec, run_many + + +def check(plugin, root): + record = json.loads((root / "expectile-expected.json").read_text()) + data = NumericData(record["values"], np.arange(6), ("feature",)) + p = Problem( + data, + np.array(record["target"])[:, None], + data.row_ids, + weight=record["weight"], + offset=np.array(record["offset"])[:, None], + ) + objective = plugin.Expectile() + np.testing.assert_allclose(objective.base(p), [record["base"]], atol=1e-12) + result = plugin.fit(p, p, context=RunContext("expectile", 19), bins=6) + for (before, after), expected in zip(result.steps, record["trace"], strict=True): + loss, g, h = objective.geometry(p, before) + np.testing.assert_allclose(loss, expected["loss"], atol=1e-12) + np.testing.assert_allclose(g, expected["gradient"], atol=1e-12) + np.testing.assert_allclose(h, expected["curvature"], atol=1e-12) + np.testing.assert_allclose(after[:, 0], expected["raw"], atol=1e-12) + outcome = run_many([RunSpec(RunContext("expectile", 19), p, p, plugin.fit, {"bins": 6})])[0] + assert outcome.error_type is None + np.testing.assert_array_equal(outcome.result.state.train_raw, result.state.train_raw) + result.state.model.save(root / "expectile-model.json") + report = dict( + passed=True, + values=record["values"], + raw=result.state.train_raw.tolist(), + rounds=result.stop.completed_rounds, + reason=result.stop.reason, + max_abs_error=float( + np.max(np.abs(result.state.train_raw[:, 0] - record["trace"][-1]["raw"])) + ), + ) + (root / "expectile-checks.json").write_text(json.dumps(report, indent=2) + "\n") + + +if __name__ == "__main__": + import ob_expectile + + assert "site-packages" in ob_expectile.__file__ + check(ob_expectile, Path(sys.argv[1])) diff --git a/examples/v1_extensions/expectile_oracle.py b/examples/v1_extensions/expectile_oracle.py new file mode 100644 index 0000000..93d2dff --- /dev/null +++ b/examples/v1_extensions/expectile_oracle.py @@ -0,0 +1,37 @@ +"""Independent D1 geometry and exhaustive-tree traces for installed checks.""" + +import json +import sys +from pathlib import Path + +import numpy as np +from tests.v1.reference.author import expectile, expectile_base +from tests.v1.reference.tree import fit_tree + + +def evidence(): + values = [[0], [1], [2], [3], [4], [None]] + x = np.asarray(values, dtype=float) + y = np.array([-3, 0, 2, 2, 90, 7.0]) + weight = np.array([2, 1, 3, 1, 0, 2.0]) + offset = np.array([1, -1, 0, 2, 1, -2.0]) + base = expectile_base(y - offset, weight=weight) + raw = np.full(6, base) + trace = [] + for _ in range(2): + loss, g, h = expectile(raw + offset, y, weight=weight) + tree = fit_tree(x, g, h, weight=weight, max_depth=2) + raw = raw + 0.1 * tree.predict(x) + trace.append(dict(loss=loss, gradient=g.tolist(), curvature=h.tolist(), raw=raw.tolist())) + return dict( + values=values, + target=y.tolist(), + weight=weight.tolist(), + offset=offset.tolist(), + base=base, + trace=trace, + ) + + +if __name__ == "__main__": + Path(sys.argv[1]).write_text(json.dumps(evidence(), indent=2, allow_nan=False) + "\n") diff --git a/examples/v1_extensions/ordered_checks.py b/examples/v1_extensions/ordered_checks.py new file mode 100644 index 0000000..114c241 --- /dev/null +++ b/examples/v1_extensions/ordered_checks.py @@ -0,0 +1,72 @@ +"""Check installed ordered recipes against separate frozen reference traces.""" + +import json +import sys +from functools import partial +from pathlib import Path + +import numpy as np +import ob_ordered_updates as ordered + +from openboost import NumericData, Problem, RunContext +from openboost.runs import RunSpec, run_many + +assert "site-packages" in ordered.__file__ +root = Path(sys.argv[1]) +source = json.loads((root / "ordered-expected.json").read_text()) +x = NumericData(source["values"], np.arange(6), ("feature",)) +predictions, errors, versions = {}, [], {} +for i, case in enumerate(source["records"]): + p = Problem( + x, + np.array(source["target"])[:, None], + x.row_ids, + weight=source["weight"], + raw_width=2, + structure={"x": np.array(source["structure"])[:, None]} + if case["family"] == "formula" + else {}, + ) + recipe = getattr(ordered, case["family"]) + options = {"mode": case["mode"]} if case["family"] == "normal" else {} + context = RunContext(f"ordered-{i}", 7) + result = recipe(p, p, context=context, rounds=3, bins=6, order=case["order"], **options) + for actual, expected in zip((s for r in result.steps for s in r), case["trace"], strict=True): + np.testing.assert_allclose(actual.before.train_raw, expected["raw_before"], atol=1e-9) + np.testing.assert_allclose(actual.after.train_raw, expected["raw_after"], atol=1e-9) + assert list(actual.coefficients) == expected["coefficients"] + assert (actual.before is not actual.after) == expected["accepted"] + errors.append(float(np.max(np.abs(actual.after.train_raw - expected["raw_after"])))) + name = f"ordered-{i}" + result.state.model.save(root / f"{name}.json") + predictions[name] = result.state.model.predict(x).tolist() + versions[name] = result.state.version + assert result.stop.completed_rounds == 3 + assert result.state.version == sum(s["accepted"] for s in case["trace"]) + outcome = run_many( + [ + RunSpec( + context, + p, + p, + partial(recipe, order=case["order"], **options), + {"rounds": 3, "bins": 6}, + ) + ] + )[0] + assert outcome.error_type is None + assert outcome.result.state.identity == result.state.identity + assert outcome.result.stop == result.stop +(root / "ordered-checks.json").write_text( + json.dumps( + dict( + values=source["values"], + predictions=predictions, + versions=versions, + max_absolute_error=max(errors), + scheduler_status="structural result accepted; all six cases match independent execution", + ), + indent=2, + ) + + "\n" +) diff --git a/examples/v1_extensions/ordered_oracle.py b/examples/v1_extensions/ordered_oracle.py new file mode 100644 index 0000000..30ea953 --- /dev/null +++ b/examples/v1_extensions/ordered_oracle.py @@ -0,0 +1,65 @@ +"""Produce development reference traces before isolated extension execution.""" + +import json +import sys +from functools import partial +from pathlib import Path + +import numpy as np +from tests.v1.reference.coupled import formula, formula_base, normal, normal_base, step + + +def main(destination): + values = [[0], [1], [2], [3], [4], [None]] + # Six bins preserve the five distinct numeric values in this tiny fixture. + bins = np.array([[0], [1], [2], [3], [4], [np.nan]]) + y = np.array([0.5, 1, 2, 3, 5, 8]) + weight = np.array([1, 0, 2, 1, 3, 1]) + structure = np.linspace(0.2, 2, 6) + records = [] + for family, mode in (("normal", "natural"), ("normal", "ordinary"), ("formula", "full")): + for order in ((0, 1), (1, 0)): + base = ( + normal_base(y, minimum_scale=1e-6, weight=weight) + if family == "normal" + else formula_base(y, weight=weight) + ) + raw = np.broadcast_to(base, (6, 2)).copy() + objective = normal if family == "normal" else partial(formula, x=structure) + trace = [] + for _ in range(3): + for channel in order: + update = step( + bins, + raw, + y, + objective, + weight=weight, + mode="ordinary" if mode == "ordinary" else "full", + damping=0.1 if family == "formula" else 0.0, + channels=(channel,), + rates=tuple(0.1 * 0.5**j for j in range(6)), + ) + trace.append( + { + "channel": channel, + "raw_before": update.raw_before, + "raw_after": update.raw_after, + "accepted": update.accepted, + "coefficients": [t[0] for t in update.trials], + } + ) + raw = np.array(update.raw_after) + records.append(dict(family=family, mode=mode, order=order, trace=trace)) + result = dict( + values=values, + target=y.tolist(), + weight=weight.tolist(), + structure=structure.tolist(), + records=records, + ) + Path(destination).write_text(json.dumps(result, allow_nan=False, indent=2) + "\n") + + +if __name__ == "__main__": + main(sys.argv[1]) diff --git a/examples/v1_extensions/ordered_updates/README.md b/examples/v1_extensions/ordered_updates/README.md new file mode 100644 index 0000000..6be9157 --- /dev/null +++ b/examples/v1_extensions/ordered_updates/README.md @@ -0,0 +1,49 @@ +# Ordered Normal and Formula updates + +`ob-ordered-updates` is a repository-authored D4 development package using public +OpenBoost CPU operations. `normal(...)` supports ordinary and Fisher directions; +`formula(...)` uses the damped full GGN direction. Both accept `order=(0,1)` or +`(1,0)`, rounds, bins, explicit prepared input and validation patience/min_delta. +The built-in OpenBoost recipes continue to use joint updates. + +Each parameter recomputes geometry from the latest accepted state, fits one +depth-two scalar tree (or a caller-supplied learner), and tries at most six rates +`0.1 * 0.5**j`. Only finite strict training-loss descent commits. Numerical trial +failures are recorded without changing accepted models, raw values or RNG. Invalid +learner construction fails outside the numerical search. Later parameters continue +from the previous accepted state even if the preceding parameter fully rejected. + +Validation chooses the strict best model after each accepted parameter. Patience +observes once after a full sweep. Thus accepted version, parameter attempts and +completed outer rounds are distinct. Substep records retain before/after immutable +states and trial outcomes for diagnosis; this is not a memory-optimized trainer or +resume checkpoint. + +The public `sweep` operation accepts geometry, direction, loss, order and learner +callbacks. The `fit` loop is objective-independent; convenience wrappers bind +Normal/Formula geometry. No private imports or OpenBoost core edits are required. + +## Reproduce + +Run `uv run --no-sync python examples/v1_extensions/verify.py OUTPUT_DIR` from +the repository. The source-side reference generator creates immutable comparison +traces before the isolated installed package is run. The installed checks receive +these traces, not the reference implementation. The verifier records all source, +reference and wheel hashes. After removing all three extension packages, a new +interpreter verifies exact predictions from all eight saved models. + +Source tests additionally cover nonzero offsets, both parameter orders, comparison +with joint updates, full and partial rejection, recovery after a nonfinite trial, +invalid order and outer-round stopping. These are exploratory development checks, +not timed E5, held-out evidence, real quality or adoption. + +## Scheduler integration + +OrderedResult contains state, nested per-round substeps and stop metadata. Sprint +041 recorded its rejection by run_many. Sprint 042 fixes that boundary with the +structural RecipeResult protocol: no extension modification or conversion is +required. The installed checker verifies independent/scheduled equality for all +six ordered cases. Mixed built-in/ordered M=1/8/32 jobs also verify shared input, +different stopping, failure isolation, reordering, regrouping and retry. +Historical Sprint 041 artifacts retain the original failure; current passing +evidence is recorded separately. Full D5 author evaluation remains open. diff --git a/examples/v1_extensions/ordered_updates/pyproject.toml b/examples/v1_extensions/ordered_updates/pyproject.toml new file mode 100644 index 0000000..0570807 --- /dev/null +++ b/examples/v1_extensions/ordered_updates/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "ob-ordered-updates" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = ["openboost==1.0.0.dev0", "numpy>=1.24"] + +[tool.hatch.build.targets.wheel] +packages = ["src/ob_ordered_updates"] diff --git a/examples/v1_extensions/ordered_updates/src/ob_ordered_updates/__init__.py b/examples/v1_extensions/ordered_updates/src/ob_ordered_updates/__init__.py new file mode 100644 index 0000000..96cce53 --- /dev/null +++ b/examples/v1_extensions/ordered_updates/src/ob_ordered_updates/__init__.py @@ -0,0 +1,163 @@ +"""D4 ordered updates composed exclusively from public CPU operations.""" + +from dataclasses import dataclass +from functools import partial + +import numpy as np + +from openboost.artifacts import TreeTerm +from openboost.binning import prepare_training +from openboost.objectives import Formula, Normal, diagonal_direction, full_direction +from openboost.runtime import initialize, preview, propose_terms, resolve +from openboost.stats import least_squares +from openboost.stopping import StopState +from openboost.tree import depthwise + + +@dataclass(frozen=True) +class Substep: + channel: int + before: object + after: object + loss_before: float + loss_after: float + coefficients: tuple + failures: tuple + + +def validate_order(order): + order = tuple(order) + if len(order) != 2 or any(type(k) is not int for k in order) or set(order) != {0, 1}: + raise ValueError("order must be a permutation of (0,1)") + return order + + +def sweep(state, binned, *, geometry, direction, loss, order=(0, 1), learner=None): + """One outer round; later parameters read only the latest accepted state. + + A learner is fitted once per parameter. Invalid learner construction fails; + numerical candidate failures are recorded and rejected without state mutation. + """ + order = validate_order(order) + if state.train.raw_width != 2 or binned.data.identity != state.train.data.identity: + raise ValueError("two-channel state and matching prepared data required") + learner = partial(depthwise, max_depth=2) if learner is None else learner + steps = [] + for channel in order: + before = state + initial_loss, gradient, metric = geometry(state.train, state.train_raw) + if not np.isfinite(initial_loss): + raise ValueError("finite accepted training loss required") + values = np.asarray(direction(gradient, metric), dtype=float) + if values.shape != state.train_raw.shape or not np.all(np.isfinite(values)): + raise ValueError("finite aligned two-channel direction required") + tree = learner(binned, least_squares(state.train, values[:, channel])) + mapping = np.eye(2)[channel : channel + 1] + TreeTerm(tree, mapping) # Validate structural learner errors outside search. + coefficients, failures = [], [] + for attempt in range(6): + alpha = 0.1 * 0.5**attempt + coefficients.append(alpha) + try: + proposal = propose_terms(state, (TreeTerm(tree, mapping, alpha),)) + candidate = preview(state, proposal) + with np.errstate(over="raise", invalid="raise", divide="raise"): + value = float(loss(state.train, candidate.predict(state.train.data))) + if not np.isfinite(value): + raise ValueError("nonfinite candidate loss") + accepted = value < initial_loss + state = resolve(state, proposal, accept=accepted, score=loss) + except (ValueError, FloatingPointError, OverflowError) as error: + failures.append(type(error).__name__) + continue + failures.append(None) + if accepted: + break + steps.append( + Substep( + channel, + before, + state, + float(initial_loss), + float(loss(state.train, state.train_raw)), + tuple(coefficients), + tuple(failures), + ) + ) + return state, tuple(steps) + + +@dataclass(frozen=True) +class OrderedResult: + state: object + steps: tuple + stop: StopState + + +def fit( + train, + validation, + *, + context, + objective, + direction, + base, + order=(0, 1), + rounds=2, + bins=254, + prepared=None, + patience=None, + min_delta=0.0, + learner=None, +): + """Generic two-parameter ordered loop, with one stop observation per sweep.""" + order = validate_order(order) + objective.validate(train) + objective.validate(validation) + state = initialize(context, train, validation, base, score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + steps = [] + for _ in range(rounds): + state, substeps = sweep( + state, + binned, + geometry=objective.geometry, + direction=direction, + loss=objective.loss, + order=order, + learner=learner, + ) + steps.append(substeps) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return OrderedResult(state, tuple(steps), stop) + + +def normal(train, validation, *, context, mode="natural", damping=0.0, **options): + direction = partial(diagonal_direction, mode=mode, damping=damping) + direction([[0, 0]], [[1, 2]]) + return fit( + train, + validation, + context=context, + objective=Normal, + direction=direction, + base=Normal.base(train), + **options, + ) + + +def formula(train, validation, *, context, damping=0.1, **options): + direction = partial(full_direction, damping=damping) + direction([[0, 0]], [[[1, 0], [0, 1]]]) + return fit( + train, + validation, + context=context, + objective=Formula, + direction=direction, + base=Formula.base(train), + **options, + ) diff --git a/examples/v1_extensions/penalized_leaves/pyproject.toml b/examples/v1_extensions/penalized_leaves/pyproject.toml new file mode 100644 index 0000000..e274ec2 --- /dev/null +++ b/examples/v1_extensions/penalized_leaves/pyproject.toml @@ -0,0 +1,12 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "ob-penalized-leaves" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = ["openboost==1.0.0.dev0", "numpy>=1.24"] + +[tool.hatch.build.targets.wheel] +packages = ["src/ob_penalized_leaves"] diff --git a/examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py b/examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py new file mode 100644 index 0000000..5c751d5 --- /dev/null +++ b/examples/v1_extensions/penalized_leaves/src/ob_penalized_leaves/__init__.py @@ -0,0 +1,51 @@ +"""D3: externally implemented weighted pinball plus anchored quadratic leaves.""" + +import numpy as np + +from openboost.tree import depthwise + + +class PenalizedLeaves: + """Replace the public routed leaf callback while retaining recipe state/growth. + + Bisection on the monotone right derivative is independent of OpenBoost's + built-in breakpoint scan. The strictly positive penalty gives a unique leaf. + """ + + def __init__(self, *, q=0.5, penalty=1.0, anchor=0.0, grower=depthwise): + if ( + not all(np.isscalar(v) and np.isfinite(v) for v in (q, penalty, anchor)) + or not 0 < q < 1 + or penalty <= 0 + ): + raise ValueError("q in (0,1), positive penalty and finite anchor required") + self.q, self.penalty, self.anchor, self.grower = q, penalty, anchor, grower + + def solve(self, view, total=None, names=None): + mass = float(view.weight.sum()) + if not np.isfinite(mass): + raise ValueError("finite total weight required") + if mass == 0: + return float(self.anchor) + with np.errstate(over="raise", invalid="raise", divide="raise"): + low = float(self.anchor - (1 - self.q) * mass / self.penalty) + high = float(self.anchor + self.q * mass / self.penalty) + if not np.isfinite(low) or not np.isfinite(high): + raise ValueError("finite solver bracket required") + for _ in range(100): + middle = low / 2 + high / 2 + derivative = ( + self.penalty * (middle - self.anchor) + + view.weight[view.residual <= middle].sum() + - self.q * mass + ) + if derivative >= 0: + high = middle + else: + low = middle + return float(low / 2 + high / 2) + + def __call__(self, data, fields, *, row_leaf, **options): + # The external solver deliberately replaces the recipe's default leaf. + # q must also be supplied to the quantile recipe for matching split/loss semantics. + return self.grower(data, fields, row_leaf=self.solve, **options) diff --git a/examples/v1_extensions/scheduler_checks.py b/examples/v1_extensions/scheduler_checks.py new file mode 100644 index 0000000..12e53fe --- /dev/null +++ b/examples/v1_extensions/scheduler_checks.py @@ -0,0 +1,86 @@ +"""Installed D5 structural-result and independent scheduling development checks.""" + +import json +import sys +from dataclasses import replace +from pathlib import Path + +import numpy as np +import ob_ordered_updates as ordered + +from openboost import NumericData, Problem, RunContext +from openboost.binning import Binning, PreparedData +from openboost.recipes import squared +from openboost.runs import RunSpec, run_many + +assert "site-packages" in ordered.__file__ +root = Path(sys.argv[1]) +x = NumericData(np.arange(6)[:, None], np.arange(6), ("x",)) +p = Problem(x, [[0.5], [1], [2], [3], [5], [8]], x.row_ids) +distribution = replace(p, raw_width=2, offset=np.zeros((6, 2))) +prepared = PreparedData(x, bins=6) +reports = [] +for count in (1, 8, 32): + specs, expected = [], {} + for i in range(count): + train, recipe = (p, squared) if i % 2 else (distribution, ordered.normal) + valid = replace(train, target=train.target[::-1]) + context = RunContext(f"mixed-{i}", 7) + options = dict(rounds=6, bins=6, patience=1 + i % 3, min_delta=100.0) + specs.append(RunSpec(context, train, valid, recipe, options, prepared)) + expected[context.run_id] = recipe(train, valid, context=context, **options) + + def forbidden(*args, **kwargs): + raise AssertionError("shared execution refitted training preparation") + + original_fit = Binning.fit + Binning.fit = forbidden + try: + failed = replace( + specs[0], context=RunContext("bad-result", 7), recipe=lambda *a, **kw: object() + ) + groups = [ + run_many([failed, *specs]), + run_many(reversed(specs)), + run_many(specs), + tuple(r for batch in (specs[::2], specs[1::2]) for r in run_many(batch)), + ] + assert groups[0][0].error_type == "ValueError" and groups[0][0].result is None + for outcomes in groups: + for outcome in outcomes: + if outcome.run_id == "bad-result": + continue + assert outcome.error_type is None + ref = expected[outcome.run_id] + assert outcome.result.state.identity == ref.state.identity + assert outcome.result.stop == ref.stop + np.testing.assert_array_equal( + outcome.result.state.validation_raw, ref.state.validation_raw + ) + np.testing.assert_array_equal( + outcome.result.state.context.rng(1, "leaf", "sample").random(5), + ref.state.context.rng(1, "leaf", "sample").random(5), + ) + finally: + Binning.fit = original_fit + report = dict( + count=count, + failed_result_retained=True, + reorder=True, + regroup=True, + retry=True, + runs={ + key: dict( + state=fit.state.identity, + stop_round=fit.stop.completed_rounds, + reason=fit.stop.reason, + version=fit.state.version, + predictions=fit.state.validation_raw.tolist(), + ) + for key, fit in expected.items() + }, + ) + if count > 1: + assert len({v["stop_round"] for v in report["runs"].values()}) > 1 + reports.append(report) +(root / "scheduler-checks.json").write_text(json.dumps(reports, indent=2) + "\n") diff --git a/examples/v1_extensions/verify.py b/examples/v1_extensions/verify.py new file mode 100644 index 0000000..9271bfe --- /dev/null +++ b/examples/v1_extensions/verify.py @@ -0,0 +1,160 @@ +"""Build isolated development wheels, check them, then remove training plugins.""" + +import hashlib +import json +import os +import platform +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +SOURCE = ROOT / "examples/v1_extensions" + + +def digest(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def main(output): + output = Path(output).resolve() + output.mkdir(parents=True, exist_ok=True) + report = { + "schema": "openboost-v1-development-extensions-v1", + "passed": False, + "claim": "repository-authored D1/D2/D3/D4 and D5 scheduling development checks only", + "commit": subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=ROOT, text=True + ).strip(), + "dirty": bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=ROOT)), + "os": platform.platform(), + "python": platform.python_version(), + "machine": platform.machine(), + "cpu_count": os.cpu_count(), + "device": "cpu", + "threads": 1, + "commands": [], + "sources": { + str(p.relative_to(ROOT)): digest(p) + for p in sorted(SOURCE.rglob("*")) + if p.is_file() and p.suffix in (".py", ".toml") + }, + } + report["reference_sources"] = { + str(p.relative_to(ROOT)): digest(p) + for p in sorted((ROOT / "tests/v1/reference").glob("*.py")) + } + env = dict(os.environ, OMP_NUM_THREADS="1", OPENBLAS_NUM_THREADS="1", MKL_NUM_THREADS="1") + + def run(command, cwd): + report["commands"].append({"argv": command, "cwd": str(cwd)}) + result = subprocess.run(command, cwd=cwd, env=env, capture_output=True, text=True) + if result.returncode: + raise RuntimeError(result.stdout + result.stderr) + return result.stdout + + try: + with tempfile.TemporaryDirectory(prefix="openboost-v1-extensions-") as temporary: + work = Path(temporary) + wheels = work / "wheels" + for project in ( + ROOT, + SOURCE / "cohort_splits", + SOURCE / "penalized_leaves", + SOURCE / "ordered_updates", + SOURCE / "expectile", + ): + run(["uv", "build", "--wheel", "--offline", "--out-dir", str(wheels)], project) + paths = sorted(wheels.glob("*.whl")) + report["wheels"] = {p.name: digest(p) for p in paths} + run(["uv", "venv", "--python", sys.executable, str(work / "env")], work) + python = str(work / "env/bin/python") + run( + [ + "uv", + "pip", + "install", + "--offline", + "--python", + python, + "numpy==2.3.5", + *map(str, paths), + ], + work, + ) + for name in ( + "checks.py", + "core_inference.py", + "ordered_checks.py", + "scheduler_checks.py", + "expectile_checks.py", + ): + shutil.copyfile(SOURCE / name, work / name) + run([python, "-I", str(work / "checks.py"), str(output)], work) + run( + [ + sys.executable, + "-m", + "examples.v1_extensions.ordered_oracle", + str(output / "ordered-expected.json"), + ], + ROOT, + ) + run([python, "-I", str(work / "ordered_checks.py"), str(output)], work) + run([python, "-I", str(work / "scheduler_checks.py"), str(output)], work) + run( + [ + sys.executable, + "-m", + "examples.v1_extensions.expectile_oracle", + str(output / "expectile-expected.json"), + ], + ROOT, + ) + run([python, "-I", str(work / "expectile_checks.py"), str(output)], work) + report["versions"] = json.loads( + run( + [ + python, + "-I", + "-c", + "import json,importlib.metadata as m; print(json.dumps({n:m.version(n) for n in " + "['openboost','numpy','ob-cohort-splits','ob-penalized-leaves','ob-ordered-updates','ob-expectile']}))", + ], + work, + ) + ) + run( + [ + "uv", + "pip", + "uninstall", + "--python", + python, + "ob-cohort-splits", + "ob-penalized-leaves", + "ob-ordered-updates", + "ob-expectile", + ], + work, + ) + run([python, "-I", str(work / "core_inference.py"), str(output)], work) + report["plugin_free_inference"] = True + report["artifacts"] = { + p.name: digest(p) + for p in sorted(output.glob("*.json")) + if p.name != "manifest.json" + } + report["passed"] = True + except Exception as error: + report["error"] = str(error) + raise + finally: + (output / "manifest.json").write_text(json.dumps(report, indent=2) + "\n") + print("Installed D1/D2/D3/D4 and scheduling checks and plugin-free inference passed.") + + +if __name__ == "__main__": + main(sys.argv[1]) diff --git a/learnings/2026-08-15-categorical-cardinality.md b/learnings/2026-08-15-categorical-cardinality.md new file mode 100644 index 0000000..219af63 --- /dev/null +++ b/learnings/2026-08-15-categorical-cardinality.md @@ -0,0 +1,60 @@ +# 2026-08-15: Categorical Tree Cardinality + +## Context + +Categorical values are encoded into `uint8`, so `BinnedArray` can represent up +to 254 non-missing categories. Tree nodes, however, represent the categories +sent left with one `uint64` bitset. The split search previously evaluated all +categories but silently omitted category codes 64 and above from that set. +Reported gain and actual routing could therefore disagree. + +## Decision or Result + +Keep the encoding limit at 254 because non-tree consumers can use those bins, +but reject categorical tree training above 64 categories in the shared split +dispatch before either the CPU or CUDA implementation runs. This preserves the +useful `BinnedArray` capability while preventing a tree from silently learning +an unrepresentable split. + +Supporting more than 64 categories correctly requires a multiword bitset (or a +different category-set representation) across split search, partitioning, CPU +prediction, CUDA prediction, tree storage, and persistence. It is a feature, +not a safe one-line limit increase. + +## Changes + +- `src/openboost/_core/_split.py`: require category counts when categorical + features are present and fail before dispatch when any count exceeds 64. +- `src/openboost/_array.py`: document the distinction between the 254-category + encoding limit and the 64-category tree-split limit. +- `tests/test_categorical.py`: prove 65 categories can be binned but cannot be + passed into categorical tree training. + +## Verification + +- Before the guard, the new 65-category training case did not raise. +- `uv run pytest -q tests/test_categorical.py tests/test_growth.py`: 46 passed. +- A focused 64-category probe separated category 63 correctly. +- `uv run ruff check src/openboost/_array.py src/openboost/_core/_split.py`: + passed. +- `git diff --check`: passed. + +## Failed Attempts + +- The first design rejected more than 64 categories inside `ob.array()`. That + conflated safe bin encoding with tree routing and would unnecessarily block + consumers such as GAM. The guard was moved to the shared tree split path. +- Linting the entire legacy categorical test file surfaced pre-existing style + issues unrelated to this change. The source files were used as the scoped + lint gate; the complete behavior files were still executed with pytest. + +## Risks and Follow-ups + +- Multiword bitsets are required before advertising native high-cardinality + categorical tree support. +- Real GPU verification should include category code 63 and CPU/CUDA parity; + this machine has no CUDA environment. + +## Commits + +- `356103b` — `fix: reject unrepresentable categorical tree splits` diff --git a/learnings/2026-08-15-categorical-persistence.md b/learnings/2026-08-15-categorical-persistence.md new file mode 100644 index 0000000..36df21a --- /dev/null +++ b/learnings/2026-08-15-categorical-persistence.md @@ -0,0 +1,61 @@ +# 2026-08-15: Categorical Tree Persistence + +## Context + +`GradientBoosting.save()` serialized categorical trees with the draft field +names `is_categorical` and `category_masks`. `TreeStructure` actually stores +the routing state as `is_categorical_split` and `cat_bitsets`, so those arrays +were omitted and categorical predictions could change after loading a model. + +## Decision or Result + +Tree persistence now writes the canonical routing fields and reconstructs them +through the `TreeStructure` constructor. The loader also recognizes the draft +key names in case an external state contains them. + +Serialization version 2 identifies files written with the corrected schema. +When a version 1 file advertises categorical input metadata, loading emits a +warning because a file produced by the broken serializer cannot reconstruct +the category bitsets that were never written. Such a model must be retrained +and resaved before production use. + +## Changes + +- `src/openboost/_persistence.py`: persist categorical bitsets and split flags, + restore missing/categorical arrays in the constructor, retain draft-key read + compatibility, and bump the serialization version to 2. +- `tests/test_persistence.py`: add an exact prediction round trip containing + categorical group splits and missing values; assert schema version and tree + arrays. + +## Verification + +- Before the fix, the new test failed because loaded + `is_categorical_split` was `None`. +- `uv run pytest -q tests/test_persistence.py tests/test_categorical.py`: + 34 passed. +- Focused categorical round-trip test: 1 passed. +- `uv run ruff check src/openboost/_persistence.py`: passed. +- `git diff --check`: passed. + +## Failed Attempts + +- A combined sandboxed `uv` verification could not read the shared uv cache; + rerunning with the already approved uv cache permission completed normally. +- The first lint pass found a local `numpy` import that shadowed the module + import inside `_from_state_dict`; removing the redundant local import fixed + the scope error. + +## Risks and Follow-ups + +- Version 1 categorical files created by the broken serializer are not + repairable because their bitsets are absent; the warning is detection, not a + migration. +- Category routing uses one `uint64` bitset. Inputs with more than 64 category + codes are currently accepted elsewhere but cannot be represented correctly; + add a fail-fast guard or implement multiword bitsets before claiming support + above 64 categories. + +## Commits + +- `aecf33f` — `fix: preserve categorical tree state on save` diff --git a/learnings/2026-08-15-performance-gate.md b/learnings/2026-08-15-performance-gate.md new file mode 100644 index 0000000..5fcdf81 --- /dev/null +++ b/learnings/2026-08-15-performance-gate.md @@ -0,0 +1,67 @@ +# 2026-08-15: Performance Regression Gate + +## Context + +The performance CI expected `benchmarks/results/performance_baselines.json`, +but that file was ignored and absent from fresh checkouts. On a missing file, +the script benchmarked the current commit, saved that same result as its own +baseline, and exited successfully. The advertised regression gate was therefore +a no-op on every fresh GitHub runner. + +## Decision or Result + +Compare the code before a push with the code after the push on the same GitHub +runner instead of committing an absolute timing baseline from unrelated +hardware. The baseline revision is `github.event.before`, not `HEAD^`, so one +workflow covers every commit in a multi-commit push to main. + +Both revisions run through the current fixed harness with separate Numba cache +directories. Raw parent/current JSON files are uploaded and include commit, +runtime versions, platform, backend, and relevant Numba environment fields. +Missing baselines now exit with status 2 before doing expensive work; creating a +local baseline requires an explicit flag. + +## Changes + +- `benchmarks/check_performance.py`: add explicit baseline/output/source-root + options, benchmark-only mode, provenance, and fail-closed missing-baseline + behavior. +- `.github/workflows/unit-tests.yml`: fetch history, create a detached worktree + at the previous remote main, benchmark old/current code on one runner with + isolated caches, and upload both artifacts. +- `tests/test_performance_check.py`: cover equal results, runtime/quality + regressions, explicit baseline loading, and provenance. + +## Verification + +- Missing-baseline CLI check exited 2 in 0.2 seconds and created no baseline. +- Unit suite: 4 passed. +- Ruff and workflow YAML parsing: passed. +- End-to-end temporary-worktree simulation: + - baseline commit `6440e31143e4cbd56e9d523a25a8f48bca302670`; + - current commit `077211066af1956f38e2a7183cd77bdd0ace140c`; + - both artifacts contained provenance and comparison returned no regressions. +- The temporary worktree was removed after verification. + +## Failed Attempts + +- A committed baseline generated on this Intel macOS host was rejected as a CI + design because absolute timings are not portable to GitHub's Linux runners. +- Comparing only `HEAD^` was rejected because a push containing several commits + would test only the final commit. `github.event.before` represents the actual + remote-main baseline for the pushed range. + +## Risks and Follow-ups + +- Shared hosted runners remain noisy. The current median-of-three and 20% + threshold are a regression alarm, not publication-quality performance proof. +- Parent and current code use dependencies installed from the current checkout; + this isolates source regressions but does not detect dependency-only speed + changes. +- The workflow must run on GitHub once to validate hosted-runner behavior and + artifact upload. External ScoringBench results remain the value proof; this CI + microbenchmark is maintenance infrastructure. + +## Commits + +- `30b9ab5` — `ci: compare performance across pushed revisions` diff --git a/learnings/2026-08-15-repository-audit.md b/learnings/2026-08-15-repository-audit.md new file mode 100644 index 0000000..6c6ad81 --- /dev/null +++ b/learnings/2026-08-15-repository-audit.md @@ -0,0 +1,67 @@ +# 2026-08-15: Repository Audit and Product Focus + +## Context + +A parallel code, benchmark, release, and ecosystem audit evaluated whether +OpenBoost had a credible path to a niche comparable in clarity—not impact—to +XGBoost. The audit was read-only and used local tests plus current primary +sources for competing libraries and publication routes. + +## Decision or Result + +OpenBoost has a substantive CPU tree core and a strong distributional subsystem, +but it is not yet a trustworthy general-purpose boosting library. The product +focus is now **calibration-first distributional boosting for tabular risk**: +NaturalBoost, exposure-aware count/severity models, proper scoring, calibration, +custom distributions, and verified single-GPU acceleration. + +Generic GBDT, GAM, DART, linear leaves, Ray, multi-GPU, out-of-core, GOSS, and +train-many must not share equal product priority. GPU remains strategically +important, but the next milestone is one correct and evidenced NaturalBoost CUDA +path—not broader unverified GPU surface area. + +## Evidence + +- CPU suite at audit time: 721 passed, 32 skipped, 3 deselected. +- Total coverage: 53%; `_models/_distributional.py` 95%, distributions 79%, + CUDA backend 0%, multi-GPU 16%, distributed tree 17%. +- The only committed third-party artifact was a three-dataset, one-seed CPU + NaturalBoost/NGBoost comparison showing approximate parity, not dominance. +- The strongest external validation opportunity was ScoringBench, which accepts + probabilistic model wrappers and publishes proper-scoring leaderboards. + +## Release-Blocking Findings + +- Categorical persistence used field names different from `TreeStructure`, so a + save/load round trip could change predictions. +- Categorical binning accepted up to 254 values while routing used one 64-bit + bitset. +- GPU GAM training dropped the base score after its first prediction update. +- Ray/multi-GPU workers initialized predictions inconsistently with final model + inference, and multi-GPU child histograms were approximate. +- The documented memmap out-of-core example passed a feature-major array to a + sample-major high-level API; `batch_size` was not connected to model training. +- The performance CI baseline was absent and regenerated on fresh runners, so + the check could succeed without detecting regressions. + +These findings must be re-verified against current code before fixing; this +entry records the audit state, not permanent truth. + +## Product and Evidence Gates + +1. Remove silent correctness failures. +2. Establish deterministic CPU reference behavior. +3. Verify end-to-end CPU/CUDA NaturalBoost parity. +4. Submit full-suite ScoringBench results with raw artifacts. +5. Add a real exposure-aware insurance case study such as freMTPL2. +6. Seek external users and contributions before JOSS/JMLR software submission. + +## Risks and Follow-ups + +- Distributional boosting mostly models aleatoric uncertainty; it does not by + itself solve epistemic/OOD uncertainty. +- PGBM, XGBoostLSS, LightGBMLSS, NGBoost, CatBoost uncertainty, and Py-Boost + already occupy adjacent positions. GPU probabilistic boosting alone is not a + unique claim. +- A benchmark is allowed to falsify the product hypothesis. Quality regressions + cannot be traded for speed without an explicit decision metric. diff --git a/learnings/2026-08-15-scaling-boundaries.md b/learnings/2026-08-15-scaling-boundaries.md new file mode 100644 index 0000000..2b52a3c --- /dev/null +++ b/learnings/2026-08-15-scaling-boundaries.md @@ -0,0 +1,68 @@ +# 2026-08-15: Scaling Boundaries + +## Context + +The high-level `GradientBoosting` and `MultiClassGradientBoosting` APIs exposed +`batch_size`, but neither training loop read it. The large-scale guide also +passed a feature-major binned memmap directly to a high-level `fit` method that +validates sample-major input. GPU and multi-GPU pages quoted speedups without a +checked-in reproducible artifact. + +## Decision or Result + +Unsupported scale features now fail or read as experimental instead of looking +production-ready: + +- a non-`None` high-level `batch_size` raises `NotImplementedError` before fit; +- memmap and mini-batch histogram utilities remain available as low-level + building blocks, not an out-of-core model API; +- GOSS is described by its sampling behavior, without a universal speed/quality + promise; +- multi-GPU is explicitly experimental until parity and repeated two-/four-GPU + measurements exist; +- numeric GPU speedup tables were removed in favor of an evidence checklist. + +## Changes + +- `src/openboost/_models/_boosting.py`: validate the reserved batch parameter in + single-output and multiclass fits; remove unsupported performance wording. +- `tests/test_large_scale.py`: cover both high-level model families. +- `docs/user-guide/training/large-scale.md`: replace the broken out-of-core + recipe with a capability/status matrix and evidence gate. +- GPU setup, installation, sklearn docstrings, model guide, and GPU example: + align public wording with the actual support boundaries. + +## Verification + +- Before the fix, both new high-level tests failed because no exception was + raised and training proceeded while ignoring `batch_size`. +- Focused batch-size tests: 2 passed. +- `uv run pytest -q tests/test_large_scale.py tests/test_core.py + tests/test_losses.py`: 77 passed, 3 expected bin-count warnings. +- `uv run mkdocs build`: passed with the repository's 29 existing griffe + warnings. +- Focused source Ruff checks, example compilation, and `git diff --check`: + passed. + +## Failed Attempts + +- `uv run mkdocs build --strict` stopped on 29 existing griffe warnings in API + docstrings across callbacks, distributions, losses, models, arrays, trees, + and importance helpers. The current docs workflow is non-strict, so the + matching build was used for this change. Strict docs cleanliness remains a + separate maintenance task. + +## Risks and Follow-ups + +- The low-level memmap and mini-batch helpers are not proof of end-to-end + out-of-core training. Implement and test a real loop before reintroducing that + claim. +- Multi-GPU correctness is not established by documentation. Require exact + single-device parity and real two-/four-GPU artifacts before promotion. +- Run single-GPU scale-extension benchmarks on Linux/CUDA with full provenance; + this Intel macOS environment cannot provide those results. + +## Commits + +- `ee555cb` — `fix: fail fast for unsupported model batching` +- `6440e31` — `docs: mark experimental scaling boundaries` diff --git a/learnings/2026-08-15-scoringbench-integration.md b/learnings/2026-08-15-scoringbench-integration.md new file mode 100644 index 0000000..8d33563 --- /dev/null +++ b/learnings/2026-08-15-scoringbench-integration.md @@ -0,0 +1,59 @@ +# 2026-08-15: ScoringBench Integration + +## Context + +OpenBoost needed an existing third-party benchmark or competition to demonstrate +value. ScoringBench was selected because it evaluates full probabilistic +regression distributions with proper scoring rules and accepts upstream model +wrappers and result artifacts. + +## Decision or Result + +The integration has two explicitly separated protocols: + +1. `official_quality`: ScoringBench's five-fold, 3,000-row protocol for an + upstream leaderboard submission. +2. `scoringbench_scale_extension`: the same datasets/folds/metrics with a larger + sample cap to compare OpenBoost CPU, OpenBoost CUDA, and existing baselines. + +Scale-extension results must never be represented as official leaderboard +results. ScoringBench proves general probabilistic quality; it does not exercise +OpenBoost's exposure-aware API, which still needs a domain benchmark. + +## Changes + +- `benchmarks/scoringbench/openboost_wrapper.py`: upstream-shaped NaturalBoost + Gaussian wrapper using ScoringBench's shared quantile-to-PMF conversion. +- `benchmarks/scoringbench/run.py`: launcher for an unmodified ScoringBench + checkout, baseline registration, protocol labeling, and provenance manifest. +- `benchmarks/scoringbench/README.md`: environment, official track, scale track, + upstream submission, and evidence gates. +- `.gitignore`: ignore arbitrary local ScoringBench result directories. + +## Verification + +- Wrapper contract: 1 passed against ScoringBench commit + `a938a667b7839b41e9272929010573410301c0b4`. +- OpenBoost distributional regression tests: 47 passed. +- `ruff check benchmarks/scoringbench`: passed. +- Python compilation and manifest protocol classification: passed. +- Integration commit: `a4555bc` (`bench: add ScoringBench integration`). + +## Failed Attempts + +- The composer-swarm Cursor scout repeatedly failed with macOS Keychain error + `SecItemCopyMatching failed -50`. Use local inspection until its CLI + authentication is repaired; do not repeatedly retry it during one task. +- The complete ScoringBench runner is not viable on Intel macOS. ScoringBench + requires NumPy 2.x, while the available PyTorch wheel uses the NumPy 1.x ABI. + One run crashed and later attempts entered an uninterruptible kernel exit + state. The launcher now refuses this platform before importing ScoringBench. + Published CPU/CUDA runs must use Linux. + +## Risks and Follow-ups + +- Run the official full suite on Linux and submit the wrapper/results upstream. +- Run a separate large-sample curve on at least three real ScoringBench datasets. +- Add CPU/CUDA prediction parity before interpreting a CUDA timing result. +- Add freMTPL2 or another real exposure-aware case study after the third-party + quality result exists. diff --git a/learnings/2026-09-05-agent-foundation-reset.md b/learnings/2026-09-05-agent-foundation-reset.md new file mode 100644 index 0000000..55d75bb --- /dev/null +++ b/learnings/2026-09-05-agent-foundation-reset.md @@ -0,0 +1,74 @@ +# 2026-09-05: Foundation is the product; compatibility does not constrain design + +## Context + +After repeated bigger-goal reviews, the user clarified that OpenBoost should +expose algorithm components for researchers and agents. GBDT, NaturalBoost, +FormulaBoost and train-many are use cases for finding the right abstraction. +The user then requested a second thinking pass and a plan, explicitly permitting +breaking APIs and rebuilding the architecture without backward compatibility. + +## Decision or Result + +The product hypothesis is lower total cost from algorithm change to a verified, +reusable result. NaturalBoost risk modeling remains a useful application; it +does not define the entire product or force ScoringBench to precede every other +foundation task. The new plan uses F0–F5 rather than restarting historical P0. + +Existing libraries already have important extension capabilities. Reviewed +primary documentation for XGBoost objectives/parameters, LightGBM update/leaf +mutation, NGBoost distributions/scores, and Py-Boost's Python GPU customization; +links and limits are in the new plan. Task-based comparisons must allow these +capabilities and source changes rather than assume competitors are black boxes. + +Recommend ordinary Python recipes owning algorithm order, explicit run and +candidate state, reusable bulk operations, and a device execution layer. +Formula and train-many must probe the design early. This is a design hypothesis, +not implemented functionality. No requirement to adapt or retain the existing +trainer, public API, fixed tree slots, global backend or persistence format. + +## Changes + +- [Active plan](../planning/agent-boosting-foundation-plan.md): reasoning, + architecture semantics, four use-case probes, task/evaluation protocol, + bounded implementation stages and decision gates. +- [AGENTS.md](../AGENTS.md): align mission and priorities with the user's explicit + direction; distinguish current implementation facts from redesign constraints. +- Previous GPU design/execution and ScoringBench priority plan: mark historical + or superseded, preserving their raw evidence and original failed budgets. +- Learning index: make this user correction discoverable before earlier strategy. + +## Verification + +- Read code paths for the trainer's channel loop and fixed schedule interface, + experimental contracts/builders/device tree export, FormulaObjective and + `fit_trees_batch`; inspected public extension docs and formula/batch tests. +- Reviewed [P7 result](../benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md): + 12.888x warm-fit ratio remains a failed 1.2 budget on that workload. No new run. +- Documentation-only validation passed: 49 local links across seven Markdown + files exist, fenced blocks balance, and `git diff --check` is clean. The link + checker excludes code spans/blocks so Python indexing is not read as a link. + No new runtime tests, GPU jobs, external contacts or publishing. + +## Failed Attempts + +- Earlier strategy interpreted a scoped GPU overhead failure as grounds for + prioritizing the distributional vertical over the foundation. The user corrected + that ordering; do not repeat it by treating the old plan as current guidance. +- Repository-authored objective/leaf/schedule packages do not yet establish + general algorithm expressivity, agent efficiency, independent adoption or + predictive improvement. In particular, easy tasks overlap incumbent features. + +## Risks and Follow-ups + +- No clean redesign implemented or evaluated. No agent comparison or external + adoption measured. Competitor documentation is not a frozen runtime experiment. +- Breaking changes remove migration obligations, not the need for mathematical + oracles, explicit unsupported inputs, independent evidence or new-format round trips. +- Next implementation task: F0.1 task cards, then independent references and a + frozen protocol. No wholesale rewrite or further kernel tuning in this slice. + +## Commits + +- `3ac1552` — prior ScoringBench configuration fix and code-review baseline. +- This documentation slice: `docs: redesign the agent boosting foundation plan`. diff --git a/learnings/2026-09-05-foundation-construction-design.md b/learnings/2026-09-05-foundation-construction-design.md new file mode 100644 index 0000000..59020bf --- /dev/null +++ b/learnings/2026-09-05-foundation-construction-design.md @@ -0,0 +1,77 @@ +# 2026-09-05: Make foundation construction an explicit design deliverable + +## Context + +After the F0.1 task-card handoff, the user asked whether the plan included how +to build the foundation. The main plan contained architecture principles and +F1 stages, but the latest deliverable and summary emphasized tasks/evaluation. +They did not give an executor enough concrete module, data and operation detail. + +## Decision or Result + +Add an engineering construction design, not another scope reset. Task cards +define required behavior; the construction design defines how to implement it; +independent references and evaluation check the result. All A1–A13 remain required. +The architecture is a proposed implementation choice, not an implemented API. + +Choose ordinary Python recipes over a universal trainer, separately replaceable +split and leaf statistics, routed leaf row access, scalar/vector tree payloads, +versioned candidate transactions and explicit run/device ownership. CPU starts +with NumPy and suitable CPU kernels; CUDA uses resident arrays and bulk operations. +State ownership and device residency are designed before attempting fusion. + +## Changes + +- [Construction design](../planning/foundation-construction-design.md): public + module dependencies; data/binning/identity and eight small records; explicit + weighting, histogram layout and memory budgets; nine operation contracts; + three growth algorithms; topology/payload/mapping and transactional updates; + all A1–A13 composition paths; CUDA and independent/batched run semantics; + inference format and B01–B14 implementation slices with failure fixtures. +- [Main plan](../planning/agent-boosting-foundation-plan.md) and + [task cards](../planning/foundation-tasks.md): link the construction design and + distinguish independent oracle preparation from F1 product implementation. +- [Agent guide](../AGENTS.md) and learning index: make this design discoverable + for later execution without claiming new components already exist. + +## Verification + +- Before editing, confirmed the construction document did not exist; reviewed + the main architecture/F1 stages, eval gates, existing core primitives/growth, + experimental contracts, their public documentation and dispatch/boundary tests. +- Documentation validation via `UV_CACHE_DIR=/tmp/openboost-research-uv-cache + uv run --no-sync python` passed: six Markdown files, 57 resolving local links, + balanced fences, 13 application composition paths, 14 build slices, eight + data/state records and nine operation contracts. All canonical entry points + link the new design; F0.2/F0.3 and all six F1 substeps remain pending. +- `git diff --check` passed. Review corrected the symmetric-growth outline to + align and aggregate all candidates before selection, rather than combining + per-node winners; added prediction-time binning identity validation. Retained + minimum-valued cuts: `[0,0,0,1]` with two bins has a valid cut at zero, which a + strict-interior-only cut rule would incorrectly remove. +- No production code or GPU job changed. Documentation checks cannot establish + runtime correctness, quality, speed or ease of algorithm authoring. + +## Failed Attempts + +- Treating task cards plus a minimal interface sketch as the complete planning + handoff left the actual construction path implicit. Naming component layers + without their inputs/outputs, data ownership and connection paths was insufficient. +- Existing experimental contracts apply objective weights and use unhalved gain; + the new specification uses an explicit statistics adapter and half-gain convention. + Copying old test numbers unchanged would conceal a semantic mismatch. + +## Risks and Follow-ups + +- New data layout, candidate transactions, serialization and batching are design + decisions that must face F0.2 oracles and F1 use cases before F2 interface freeze. +- F0.2 and F0.3 remain next. F1 starts with minimal state and manually specified + trees, then real scalar grow and complete GBDT/Normal recipes; Formula/run-many + probe those boundaries early. Every remaining application still needs implementation. +- Performance costs, artifact readers and external extension usability remain + unverified. No changes to E0–E7 thresholds or backward-compatibility requirements. + +## Commits + +- `be373e6` — prior task-card and baseline audit slice. +- This slice: `docs: specify how to construct the v1 foundation`. diff --git a/learnings/2026-09-05-foundation-f0-task-cards.md b/learnings/2026-09-05-foundation-f0-task-cards.md new file mode 100644 index 0000000..67dc90b --- /dev/null +++ b/learnings/2026-09-05-foundation-f0-task-cards.md @@ -0,0 +1,92 @@ +# 2026-09-05: F0.1 executable specifications for every v1 use case + +## Context + +The user asked to continue after requiring individual implementation and +evaluation for every A1–A13 use case. The next committed slice is F0.1, rather +than another strategy reset or a premature runtime rewrite. + +## Decision or Result + +Complete task specifications for all applications, recipe/component mappings, +five author modifications and a minimal public-interface sketch. Explicit +mathematics and failure cases distinguish expressivity from predictive value. +F0.2 will implement independent references; F0.3 will freeze actual artifacts, +baseline builds, budgets, held-out tasks and the evaluation runner. + +Select concrete datasets and split rules for every use case, including +Concrete Compressive Strength for FormulaBoost. Composition predicts two +parameters of a monotone saturation hypothesis in age; the formula is a proposed +model, not a source-established law. A single-row two-parameter GGN has rank one; +parameter recovery needs separate identifiable synthetic fixtures. Real task +quality and wrong-formula failures remain required. + +Other consequential contracts include ranking query/pair weights, quantile +leaf access to residuals, the softmax diagonal curvature approximation, explicit +offset persistence, and separate positive-payment count/severity definitions +for aggregate-loss composition. No application can substitute for another. + +## Changes + +- [Task cards](../planning/foundation-tasks.md): A1–A13 inputs/outputs, data and + splits, two-round semantics, math, failure conditions and baselines; R1–R9 / + C1–C7 mappings, D1–D5 author tasks, device boundaries and minimal interfaces. +- [Active plan](../planning/agent-boosting-foundation-plan.md): mark only F0.1 + complete and hand off F0.2; the entire F0 and E-gates remain incomplete. +- [Application contracts](../planning/foundation-application-contracts.md): + link concrete task decisions and replace the remaining unselected A10/A12 data. +- Learning index: expose the execution milestone ahead of the planning record. + +## Verification + +- Before editing, confirmed the task-card file was absent. Read current model, + objective, batch and experimental entry points together with tests and docs. +- Primary-source audit covers current XGBoost/LightGBM/CatBoost objectives and + extension boundaries, NGBoost geometry, Py-Boost GPU customization and the + newly selected datasets; sources are linked in task cards. None of these new + baseline versions was installed or benchmarked in this slice. +- Numerical sanity using `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync python`: central finite differences matched the stated Poisson, + Gamma, Tweedie p=1.5 and right-censored log-normal AFT derivatives. The Formula + raw Jacobian matched finite differences and its single-row GGN had rank one. + The D2 fixture selected cut 1 without the cohort constraint and cut 2 with it. + These small checks do not replace F0.2 executable oracle tests or production parity. +- Documentation checks passed for all five changed Markdown files: 47 local + links resolve and code fences are balanced. Coverage checks found all 13 + application cards and matrix rows, all nine recipes, seven capabilities and + five author tasks; each application has inputs/outputs, data, failure cases + and baselines. Only F0.1 is checked off; F0.2/F0.3 remain pending. +- `git diff --check` passed. No runtime code changed and no GPU job or full + model regression suite was run. + +## Failed Attempts + +- Direct CatBoost multi-output documentation fetching timed out; the official + indexed page supplied the MultiRMSE device support table. Py-Boost's latest + release URL did not yield a release, so no version/tag was invented. +- Existing Formula code already computes a coupled direction before fitting + parameter trees. A comparison that forbids incumbent outer loops would + manufacture a false capability advantage. +- Py-Boost's actual builder sketches G/H for split selection but computes leaf + values from the original gradients. That boundary is an existing competitor + capability, not an OpenBoost invention; modifications must also update cached + train/validation predictions before the next gradient evaluation. +- A CPU-only author comparison would exclude Py-Boost unfairly. It remains an + eligible direct competitor; if selected, its arm runs on GPU with declared + device/cost under the same E5 wall/token budget. + +## Risks and Follow-ups + +- Data bytes, parsed schemas, actual group counts, licenses for downloaded + artifacts, frozen builds and runtime capability still require F0.3 verification. +- Formula misspecification, category quality and bounded-pair ranking costs may + fail their gates; retain failures instead of deleting tasks or changing targets. +- H1/H2 are reserved evaluation slots, not completed hidden tasks. The evaluation + side must freeze content/verifiers without exposing them to F1 interface design. +- Next: F0.2 independent scalar/tree references, then the other task-specific + geometries and state/run failure fixtures, without production core changes. + +## Commits + +- `7b57436` — every listed v1 use case made individually required. +- This slice: `docs: specify all v1 foundation tasks and baselines`. diff --git a/learnings/2026-09-05-foundation-fixed-slot-growth.md b/learnings/2026-09-05-foundation-fixed-slot-growth.md new file mode 100644 index 0000000..436b2ba --- /dev/null +++ b/learnings/2026-09-05-foundation-fixed-slot-growth.md @@ -0,0 +1,86 @@ +# 2026-09-05: Remove split compaction from fixed-slot growth + +## Context + +P7 found a 13.899x default GPU fit regression. The isolated host profile places +most time in LevelWiseBuilder and its session boundary. Before relaxing any +correctness checks, eliminate variable-length indexing inside fixed-slot growth. + +## Decision or Result + +Use fixed-shape selections for tree arrays. A nonroot slot enters the next +frontier iff its fixed parent split in this round. Preserve terminal leaves; +root must never reenter through the clamped parent index. No numerical split, +histogram, leaf, validation or cached-prediction contract changes. + +## Changes + +- LevelWiseBuilder uses where and parent-index gathering instead of boolean + extraction/scatter of split arrays; parent mapping is created once per tree. +- Added branching/early-leaf tests that reject boolean compaction and compare + the entire tree/predictions against the existing original-row oracle. + +## Verification + +- Both new tests failed on the original boolean extraction before the edit. +- Focused CPU builder/split/leaf/objective/dispatch regression is recorded below. +- Real CUDA trainer conformance and matched-quality P7 measurements follow a + clean implementation commit; no speedup inferred from fewer operations. + +## Failed Attempts + +- None on hardware yet. This is a bounded candidate, not a proven performance fix. + +## Risks and Follow-ups + +- All scalar checks and independent prediction traversal remain; this change may + remove only part of the measured overhead. G4 budget and G5 adoption stay open. +- Parent mapping is specific to the existing complete fixed-slot topology; + arbitrary user tree topology is not changed or supported by this rewrite. + +## Commits + +- `df835e3`: preceding P7 evidence and negative value conclusion. + +- Focused CPU regression: 80 passed. Production code and changed-test lint passed; + staged change preserves the prior numerical oracle unchanged. + +## Real CUDA correctness + +- `81acf0b`: [T4 trainer artifact](../benchmarks/results/foundation/20260905T193001Z-cece3e8f/README.md), + 7 passed / 0 skipped. CPU/CUDA gradients, splits, leaves, tree predictions, + bounded updates, Normal/Poisson ordinary/natural fits and rollback pass. +- Maximum adapter raw error 1.19e-7. Existing compact transfer counts unchanged. + Performance remains to be measured; no speed claim from correctness runtime. + +## Independent CPU package check + +- Clean `a5bf26d`: [CPU installed-wheel artifact](../benchmarks/results/foundation/p7-fixed-slot-cpu-a5bf26d/README.md), + 7 passed plus public weighted demo and six exact plugin-free CPU predictions. + Core wheel matches the GPU correctness/value bundles; plugins unchanged. +- Corrected prepare.py's descriptive profile metadata to say fifth host-profile + and sixth memory-only fit. Worker behavior already separated them; the current + running value bundle retains its original manifest verbatim. Its timed fits + are unaffected, and its per-profile memory scope records the actual separation. + +## Measured value and decision + +- `a5bf26d`: [12-cell T4 matrix](../benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md), + 3 passed / 0 skipped. All default fits pass unchanged quality/fallback gates. +- Recorded default warm median 2.078694 -> 1.868019 s (10.13% lower); paired + legacy ratio 13.899x -> 12.888x (7.27% lower). Legacy also got slightly faster, + so do not attribute every raw percentage point to this rewrite. No statistical + significance or general GPU advantage claimed from three splits. +- Keep this small optimization; it preserves all checks and original-row/tree + parity. It addresses only part of the overhead. The original large regression + remains: G4 still fails and the strict path does not replace legacy CUDA. +- Isolated seed-0 diagnostic tree/session 1.9099 s (builder 1.3641 s) versus + objective .0866 s in a 2.0466 s profile. Remaining work is in tree/session + validation/synchronization and cache traversal, not primarily objective math. +- CPU 80 focused tests, real T4 trainer 7 tests, independent wheel 7 tests with + six exact plugin-free roundtrips, and value 3 tests passed. Lint and MkDocs + passed. Exact peak/whole-process transfer trace and external adoption stay open. +- Final evidence audit: all uploaded file/wheel hashes, JUnit equality, unchanged + measured implementation, and private-path scan passed. Recorded percentage + reductions recomputed from raw medians/ratios. Final production/changed-file + lint passed; docs build completed with existing griffe warnings. diff --git a/learnings/2026-09-05-foundation-p0-integration.md b/learnings/2026-09-05-foundation-p0-integration.md new file mode 100644 index 0000000..5eb4ab7 --- /dev/null +++ b/learnings/2026-09-05-foundation-p0-integration.md @@ -0,0 +1,92 @@ +# 2026-09-05: Foundation P0 Integration + +## Context + +The user authorized execution of the foundation checklist starting at P0. +The clean design branch at `67ee05b` needed the pinned remote revision +`6ebe3a8ced0e621b17e3cf63e31721af58471053`, while preserving the local +categorical persistence/cardinality, batch fail-fast, ScoringBench and CI fixes. + +## Decision or Result + +Merge the histories on `codex/gpu-python-foundation-design`; do not move main. +Keep CLAUDE.md as a pointer to canonical AGENTS.md. Reconcile GPU setup and +installation documentation with actual backend eligibility and experimental +scaling boundaries. + +The new unified models were absent from the generic `ob.load()` class map. +Four new numeric/mixed-feature round-trip tests reproduced `Unknown model +class` for FormulaBoost and WeibullAFT. Add the two classes to that map without +changing the serialization schema or the preserved categorical state handling. + +The remote benchmark page claimed committed results but identified its source +as transcription from ignored task notes. Remove unsupported result tables +and their guide/migration summaries; retain reproduction scripts and the +older committed CPU artifact. Historical design notes are explicitly marked +unverified, not promoted to current passed gates. + +## Changes + +- Merge the remote trainer, objectives, FormulaBoost, WeibullAFT, tests, + benchmarks and documentation into the existing local history. +- [Generic loader](../src/openboost/_persistence.py): register the new model + classes for generic loading. +- [Integration regression](../tests/test_unified_persistence.py): exact + prediction and parameter round trips for both classes with numeric features + and with categorical/missing features. +- [Benchmark evidence](../docs/benchmarks.md), GPU setup, installation and + related model guides: preserve capability boundaries and require raw evidence. + +## Verification + +Commands use `UV_CACHE_DIR=/tmp/openboost-research-uv-cache` because the default +uv cache is not writable in the sandbox. `OPENBOOST_BACKEND=cpu` selects CPU +for all local model tests. Python 3.12.12 on Intel macOS. + +- `uv sync --locked --extra dev`: passed after retrying outside the sandbox + because the first attempt could not resolve PyPI DNS. +- Red test: `uv run --no-sync pytest tests/test_unified_persistence.py -n 0 -q`: + **4 failed**, each because the corresponding class was absent from ob.load. +- Targeted merge regression: `uv run --no-sync pytest tests/test_categorical.py + tests/test_persistence.py tests/test_batch.py tests/test_extensibility.py + tests/test_formula.py tests/test_survival.py -n 0 -q`: **93 passed, 1 GPU + skipped**, 313.77 seconds. +- After the loader fix: `uv run --no-sync pytest tests/test_unified_persistence.py + tests/test_persistence.py -n 0 -q`: **21 passed**, 25.12 seconds. +- Full CPU regression: `uv run --no-sync pytest tests/ -m "not gpu and not + benchmark" --tb=short`: **749 passed, 34 skipped**, 545.68 seconds. This run + collected before the four new loader cases were added; those and the final + loader change are covered by the 21-test focused run above. Skips include + unavailable CUDA/multi-GPU, JAX and plotting dependencies, not successful + validation of those capabilities. +- `uv run --no-sync ruff check src/openboost/ tests/test_unified_persistence.py`: + passed. `git diff --cached --check`: passed. +- `uv run --no-sync mkdocs build`: passed, with the same 29 existing griffe + docstring warnings recorded in the earlier scaling-boundaries learning. +- `uv build`: passed, producing both sdist and wheel after the loader fix. +- Source comparison confirms `_array.py`, `_core/_tree.py`, `_models/_boosting.py`, + `.github/` and `benchmarks/scoringbench/` retain the local pre-merge content. + The only additional persistence change is the two loader registrations. + +## Failed Attempts + +- The merge produced the three expected documentation/instruction conflicts; + there were no production-code text conflicts. Clean merges still required + semantic review, which found the generic-loader integration gap. +- Sandboxed dependency sync failed to fetch hatchling due to DNS restrictions; + the authorized network retry succeeded with the same lockfile. + +## Risks and Follow-ups + +- Local CPU checks do not validate CUDA or the planned device-resident + extension path. P1 establishes the stricter Modal test harness. +- The possible weighted constant-Hessian mismatch, device dispatch and + fallback behavior remain P2 work. They are not claimed fixed here. +- Benchmark result tables need committed raw artifacts before reinstatement. +- Existing API-docstring warnings remain separate from merge correctness. + +## Commits + +- `67ee05b` — local parent containing the approved design. +- `6ebe3a8` — pinned remote parent containing the unified trainer. +- This entry accompanies the verified P0 merge commit. diff --git a/learnings/2026-09-05-foundation-p1-modal.md b/learnings/2026-09-05-foundation-p1-modal.md new file mode 100644 index 0000000..e1a6eb6 --- /dev/null +++ b/learnings/2026-09-05-foundation-p1-modal.md @@ -0,0 +1,92 @@ +# 2026-09-05: Foundation P1 Modal Verification + +## Context + +The user requested continuation after P0. P1 must establish wheel-installed, +traceable GPU smoke tests and fail on missing/skipped tests or silent fallback. +Modal use was already authorized. Local CPU success is not GPU verification. + +## Decision or Result + +Use a dedicated `benchmarks/foundation/modal_app.py` instead of registering +foundation jobs on the legacy app: the legacy app registers source-mounted +jobs and broad dependencies, so reuse would build unrelated images and weaken +the installed-wheel boundary. Legacy entrypoints remain unchanged. The design +and execution checklist now reflect this implementation decision. + +Pin the Linux amd64 CUDA 12.4.0 devel Ubuntu 22.04 image by registry digest; +export hash-locked CUDA/test dependencies from uv.lock, excluding unused JAX. +Use uv 0.12.1 for image installation, Python 3.12, a single T4, two CPU cores, +8 GiB requested memory, one container, retry=0 and timeout=300 seconds. + +Prepare from a clean commit; verify all copied inputs and installed Python +package contents against the wheel. Disable automatic Modal source inclusion. +Two mandatory tests exercise CuPy/Numba pointer ownership with a real kernel +and the two-round Normal objective/native-tree call path. This is a smoke, +not complete CPU/CUDA parity, a held-out quality result, or a speed benchmark. + +## Changes + +- [Bundle preparer](../benchmarks/foundation/prepare.py): allowlisted upload, + clean source SHA, wheel/data protocol and dependency hashes. +- [Offline validator](../benchmarks/foundation/runner.py): reject failures, + timeouts, missing/duplicate/skipped cases, bad provenance and missing device + execution evidence. CLI returns nonzero for invalid/missing reports. +- [Modal app](../benchmarks/foundation/modal_app.py): isolated image and bounded + subprocess, environment/version collection and result persistence before + validation. Image-build failures precede the local entrypoint and must be + recorded separately. +- [GPU tests](../tests/foundation/test_smoke.py), isolated pytest config and + conftest; [host contract tests](../tests/test_foundation_runner.py). +- Explicit result-format allowlist in benchmarks/results/.gitignore; + [reproduction instructions](../benchmarks/foundation/README.md). + +## Verification + +Local commands use `UV_CACHE_DIR=/tmp/openboost-research-uv-cache`. +The host contract test initially failed collection because the implementation +module did not exist; after implementation, all 12 tests passed. They exercise +failure propagation and completeness, not GPU execution. + +`uv run --no-sync ruff check src/openboost/ benchmarks/foundation +tests/foundation tests/test_foundation_runner.py` passed, as did staged +whitespace checks. The wheel was built from clean harness commit `3101a44`. + +The authorized single-T4 smoke completed successfully: **2 passed**, no skips, +Modal CLI exit 0. Offline revalidation with `python -m +benchmarks.foundation.runner` also passed. Raw evidence and reproduction: +[20260905T080803Z-c5ced00e](../benchmarks/results/foundation/20260905T080803Z-c5ced00e/README.md). + +Verified 37 installed Python files against the wheel, device pointer/lifetime +interop, 2 device objective calls and 4 native tree calls. Remote function wall +time was 14.66 seconds (not billed GPU duration); pytest was 9.16 seconds. +The T4 driver was 580.95.05. CuPy reported CUDA runtime 12.9 despite the pinned +12.4 toolkit base; record runtime, toolkit and driver separately. Small-grid +under-utilization warnings are expected for this smoke. + +## Failed Attempts + +- Installed Modal 1.3.0.post1 has no `uv_pip_install_from_requirements` method; + use its documented `uv_pip_install(requirements=[...])` API, confirmed by + inspecting the installed SDK implementation. +- The first lint found an import ordering issue in the host test; corrected. +- Sandboxed wheel preparation could not resolve PyPI for hatchling. The same + clean-source preparation succeeded with the authorized network permission. + +## Risks and Follow-ups + +- CUDA smoke passed for the harness commit. P2 still owns weighted Hessian, + kernel fallback and full gradient/split/leaf/task parity checks. +- Function wall time excludes image startup/build and is not billed GPU time. +- The container reports architecture, visible CPU count and host RAM alongside + requested limits. Exact physical CPU model was not collected; add it to P2 + performance provenance when available. No speed claim is made here. +- The smoke's private imports/spies are test instrumentation; P6 external + extension packages must use only the documented public API. + +## Commits + +- `f7eaf25` — P0 integration baseline. +- `3101a44` — harness implementation, independently verified before GPU use. +- Raw GPU evidence is committed separately from its source to avoid circular + source hashes. diff --git a/learnings/2026-09-05-foundation-p2-boundaries-baseline.md b/learnings/2026-09-05-foundation-p2-boundaries-baseline.md new file mode 100644 index 0000000..7391b0c --- /dev/null +++ b/learnings/2026-09-05-foundation-p2-boundaries-baseline.md @@ -0,0 +1,116 @@ +# 2026-09-05: P2 execution boundaries and baseline + +## Context + +P2.1 passed on T4 at 8609f70. Continue with execution boundaries and the planned +real-data baseline, preserving wheel-only provenance and historical artifacts. + +## Decision or Result + +A separate boundaries suite preserves historical correctness-test semantics. +Eight new GPU cases join the five existing ones: custom same-name distribution, +exposure, generic tree fallback, runtime error rollback, two sampling preflights +and two callback/eval/persistence cases. No production code changed here. + +Baseline configuration and gates are frozen before any GPU baseline result. +California Housing uses sklearn 1.8.0's archive URL/SHA-256 and transformations. +Its archive, float32 arrays and seed 0/1/2 splits have committed hashes in +`benchmarks/foundation/housing.json`. The public archive is stored in ignored +`build/foundation_data/`, never silently replaced with synthetic data. + +The baseline model uses 30 rounds, depth 3, learning rate .05 and 64 bins. +Each 60/20/20 split has 12,384 training and 4,128 validation/test rows. No learned +scaling; only training data fits bins. CPU/CUDA × three seeds × no-eval/eval +produces 12 fresh subprocesses, two fits each. Every process gets a fresh +NUMBA_CACHE_DIR; first/repeat fit times include binning, objective math, copies, +compilation and path instrumentation, but exclude imports/loading/startup. +CUDA reductions use rtol=2e-5/atol=2e-6 repetition checks, not bitwise identity. + +A single baseline Modal run first executes all 13 correctness/boundary cases +with maxfail=1, and only then the matrix. Per-cell runtime limit is 150s, +pytest limit 1740s and function limit 1800s, one T4, CPU=2, memory=8192 MiB, +retry=0. It preserves partial cells and failure output. This is a bounded +baseline, not a scaling experiment or a speed claim. + +## Changes + +- `tests/foundation/test_boundaries.py`: actual CUDA execution and visible + fallback; CPU comparisons; GPU-save/CPU-load and reverse round trips. +- `dataset.py` / `housing.json`: public archive loader and frozen data/splits. +- `baseline_worker.py`: end-to-end fit/predict, independent Normal metrics, + cold/repeated fit policy and execution-path instrumentation. +- `test_baseline.py`: ordered matrix and predeclared NLL/CRPS/coverage gates. +- Preparer/Modal/runner: allowlisted baseline bundle, complete matrix gate, + CPU model provenance when exposed, and separate timing scopes. +- `tests/test_foundation_baseline.py`: missing/duplicate/fallback/device/metric/ + timing/quality rejection, split isolation and independent metric oracle. +- `benchmarks/foundation/README.md`: protocol, reproduction and limits. + +## Verification + +Local commands use `UV_CACHE_DIR=/tmp/openboost-research-uv-cache`. + +- `OPENBOOST_BACKEND=cpu uv run --no-sync pytest tests/test_foundation_baseline.py + tests/test_foundation_runner.py -n 0 -q`: **27 passed**. +- `uv run --no-sync ruff check benchmarks/foundation tests/foundation + tests/test_foundation_baseline.py tests/test_foundation_runner.py`: passed. +- Isolated pytest collection verifies 14 cases in order: smoke, weighted + correctness, eight boundaries, then baseline. Collection is not a GPU pass. +- Downloaded archive SHA-256 matches the pinned sklearn value. Its loader output + exactly matches sklearn 1.8.0 after float32 conversion (all features/targets). + The reference fetcher received a local verified copy, avoiding another download. +- Local real-data CPU seed-0 resident and eval development cells completed; + first/repeat and eval/no-eval predictions were bit-identical, and evaluation + logged all 30 rounds. No development timing is promoted to baseline evidence: + these checks ran before the harness commit and on a different local CPU. + +## Real-device verification + +- Tested clean source `99621ae9e640be9c1144a015a39de43d3bf50ae9`, same production + wheel as P2.1; [raw artifact](../benchmarks/results/foundation/20260905T084129Z-2574e387/README.md). +- **14 passed, 0 skipped**, pytest 104.58s, remote function 108.88s. +- All eight execution boundaries passed, including visible custom/exposure/ + generic fallback, failed-kernel rollback, sampling preflight, callback/eval + and both CPU↔GPU persistence directions for Normal/Poisson. +- All 12 CPU/CUDA × seed × eval-mode cells completed two fits. Maximum held-out + NLL difference 1.34e-8, CRPS difference 1.12e-8, coverage difference zero. + No fallback warnings; each GPU fit uses 30 objective calls and 60 native trees. +- Offline validator accepted the result. Raw artifacts contain no local user + paths or private Modal app URLs. Copied source hashes, frozen dataset/ + split metadata and exact JUnit content independently match the source commit. +- Median repeated fit: CPU 2.401s / GPU .145s without eval; CPU 3.027s / GPU + .230s with eval. These measurements are scoped in the artifact and do not + establish a general library-level speed claim. + +## Failed Attempts + +- Automatic review rejected the P2.2 Modal command because the new test file + was outside the previously approved manifest, despite the wheel being + byte-identical to the previously approved wheel. No remote job started. + The user subsequently explicitly approved the new files. The original Modal + upload/run command then succeeded; no rerouting was used. +- Sandboxed public-data download failed DNS. A reviewed network-only download + succeeded and its archive hash verified. No project content was uploaded. +- Lint rejected a lambda assignment in the worker; replaced it with a function. + +## Risks and Follow-ups + +- P2.2 and P2.3 now pass on the tested T4 source. Next gate is P3, the minimal + CPU extension contract; do not infer new backend/extension capabilities from + this existing-trainer baseline. +- Nominal 90% interval coverage is 96.39–96.78% in this untuned baseline. + CPU/GPU parity is excellent, but calibration and tuned product quality remain + separate work. First-fit timing does not clear CUDA driver caches; /proc + exposes CPU model as `unknown`. Neither missing fact is invented. +- The trainer download spy/counter covers only named trainer boundaries; + compact tree conversion and backend internals can still copy. No complete + PCIe accounting, GPU peak memory measurement or zero-transfer claim. +- Per-seed NLL difference <= .01*max(1,abs(CPU NLL)), CRPS regression <= 1%, and + coverage90 difference <= .01 follow the design. They cannot waive strict + micro-oracle failures, and three seeds are not a significance claim. + +## Commits + +- `576702d` — P2.1 before/after T4 evidence. +- `4423117` — eight-case execution boundary harness. +- `99621ae` — frozen baseline harness, now verified on real T4. diff --git a/learnings/2026-09-05-foundation-p2-correctness.md b/learnings/2026-09-05-foundation-p2-correctness.md new file mode 100644 index 0000000..881b86d --- /dev/null +++ b/learnings/2026-09-05-foundation-p2-correctness.md @@ -0,0 +1,112 @@ +# 2026-09-05: Foundation P2 correctness + +## Context + +P2 starts with the suspected weighted constant-Hessian optimization conflict. +The unified objective weights both gradients and Hessians; the native hint +previously depended only on the unweighted objective's unit-Hessian property. +P2 also requires explicit capabilities, visible fallback and scoped seeds. + +## Decision or Result + +The initial seven host regressions failed: same-name custom distribution +misclassification, swallowed kernel errors, GPU sampling preflight (two +parameters), failed-first-step fitted state and missing model seed support +(two sampling parameters). They pass after the changes below. + +A host interception test verifies that uniform and nonuniform weights now +select const_hess=0, while absent weights retain const_hess=1. This verifies +policy; subsequent real-T4 runs now confirm the mathematical regression. +Pre-fix source `502f37f`: **3 failed, 2 passed**. Fixed source `8609f70`: +**5 passed**, no skips. Native weighted histogram sums changed from the wrong +`[4, 4]` to CPU/analytic `[7, 10]`; weighted Normal/Poisson raw differences +changed from 0.551514 / 0.0934991 to zero on these fixtures. P2.1 is verified; +the remaining P2 GPU boundaries and real-data baseline subsequently passed; +see [P2 completion](2026-09-05-foundation-p2-boundaries-baseline.md). + +## Changes + +- `benchmarks/foundation`: add an isolated correctness suite alongside smoke. + Require all smoke cases plus fixed-bin weighted Newton and Normal/Poisson. +- `tests/foundation/test_correctness.py`: include zero and nonuniform weights, + inspect the native histogram hint and check analytic Newton predictions; + compare CPU/device distribution gradients, splits, raw scores and NLL. +- `_trainer.py`: disable the unit-Hessian hint for weighted fits; validate + sampling before binning; reject GPU sampling until verified; warn about host + objectives and generic tree fallback. Restore trainer-owned state on errors. +- `_objectives.py`: only exact built-in distribution types select built-in + kernels; let runtime and compilation errors propagate without a host retry. +- NaturalBoost/DistributionalGBDT, FormulaBoost and WeibullAFT now accept + `random_state`. The unified trainer creates one local generator per fit, + shared by row and column sampling. Core callers without a generator retain + their legacy behavior; this is not a global legacy-RNG migration. +- Document current execution boundaries and preserve unweighted initialization + semantics. No new performance or quality claims. + +## Verification + +All commands use `UV_CACHE_DIR=/tmp/openboost-research-uv-cache`. + +- `OPENBOOST_BACKEND=cpu uv run --no-sync pytest + tests/test_foundation_contracts.py -n 0 -q`: initial **7 failed**, then + expanded suite **12 passed**, including native-hint interception, visible + host fallback, global RNG isolation and seed persistence/refit round trip. +- `OPENBOOST_BACKEND=cpu uv run --no-sync pytest + tests/test_foundation_contracts.py tests/test_foundation_runner.py + tests/test_distributional.py tests/test_distribution_gradients.py + tests/test_formula.py tests/test_survival.py tests/test_growth.py + tests/test_unified_persistence.py -n 0 -q`: **171 passed, 2 skipped** in + 39.62 seconds. The two skips require optional JAX; no GPU success is inferred. + The additional seed persistence case was added and verified afterward. +- `uv run --no-sync ruff check src/openboost benchmarks/foundation + tests/foundation tests/test_foundation_contracts.py + tests/test_foundation_runner.py`: passed. +- `uv run --no-sync mkdocs build`: passed with existing griffe warnings. +- `uv build --offline`: wheel and source distribution built successfully. + +Real GPU artifacts: + +- [Pre-fix failure](../benchmarks/results/foundation/20260905T082151Z-78e83b7e/README.md): + identical test/config hashes, T4, CLI exit 1, failure retained. +- [Fixed success](../benchmarks/results/foundation/20260905T082025Z-ef9e0c4b/README.md): + T4, CLI exit 0, offline validation passed. +- Verified both artifacts' copied-file hashes against their source commits; + GPU model, packages, CUDA runtime/driver and thread settings match. The + offline validator accepts the green result and rejects the red result. +- Documentation rebuild passed after removing the broken evidence link. + Staged whitespace checks exclude the red run's raw `junit.xml`: pytest + failure tracebacks contain trailing spaces, preserved byte-for-byte and + verified equal to the report embedded in `results.json`. +- Weighted fixture inputs are fully specified in the hash-pinned test source. + This is not real-data quality or timing evidence. + +## Failed Attempts + +- Automatic approval review rejected the combined clean-wheel prepare / Modal + command because it considered uploading this specific source-derived wheel + and test bundle insufficiently explicitly authorized. No remote job ran. + Do not reroute or indirectly perform the upload. Request explicit approval + for the allowlisted bundle after completing local work. The user explicitly + approved that upload in the next turn and it succeeded. +- Historical-run upload was initially rejected as a separate payload. Hash + verification proved its wheel identical to already-uploaded P1 and all test/ + config files identical to the just-approved bundle. Re-review with that + evidence allowed the same command; no workaround was used. +- Lint found two nested context managers in new tests; combined them and reran. +- A public-guide relative link to repository-only benchmark artifacts caused a + MkDocs missing-target warning. Kept evidence links in the repository learning + and artifact READMEs and removed the redundant guide verification claim. + +## Risks and Follow-ups + +- GPU boundary checks and the frozen real-data baseline are now complete in + the [follow-up entry](2026-09-05-foundation-p2-boundaries-baseline.md). P3 is + the next gate; this does not validate a new experimental API yet. +- Rollback covers the trainer's assigned state. Arbitrary callback side effects + and facade state modified before entering the trainer are outside that scope. + +## Commits + +- `1669974` — previous passing P1 evidence. +- `502f37f` — pre-fix weighted CUDA regression harness. +- `8609f70` — locally verified fixes; now also verified by the T4 artifact. diff --git a/learnings/2026-09-05-foundation-p3-cpu-contract.md b/learnings/2026-09-05-foundation-p3-cpu-contract.md new file mode 100644 index 0000000..d4bf3eb --- /dev/null +++ b/learnings/2026-09-05-foundation-p3-cpu-contract.md @@ -0,0 +1,128 @@ +# 2026-09-05: P3 CPU extension contract + +## Context + +P2 baseline passed at 99621ae; P3 introduces a small CPU extension facade while +retaining the existing trainer loop. The user requested continuation. + +## Decision or Result + +Implement in three verified slices: objective/arrays, builder/schedule, then +persistence/early-stop consistency. CPU-only execution is explicit; requested +CUDA either fails preflight or warns and selects the entire CPU path. + +## Changes + +- `experimental.Booster`, immutable `ExecutionContext`, shared `TrainerConfig` + and a distribution adapter. The facade invokes the existing trainer. +- Validate complete channel keys, contiguous float32 statistics, nonnegative + curvature, finite data/weights, positive total weight and explicit devices. + Inputs are read-only views; aliased outputs fail without per-round host copies. +- Add min_gain to the single trainer config and propagate it to tree builders; + a supplied per-fit generator can be shared with extension contexts. + +## Verification + +- Initial independent two-channel objective test failed import before creation. +- The first implementation exposed a shared weight-validator boundary: it + allows all-zero weights and Inf. The experimental facade now rejects them + before binning without changing legacy validation semantics. +- Slice 1: 53 passed (23 experimental, plus existing foundation/formula/survival + tests); production/new-test lint passed. No experimental GPU execution is + claimed by P3. + +## Builder/update slice + +- Explicit builder dispatch precedes native eligibility; every channel uses a + single round-start objective evaluation. Schedule values are full coefficients + stored alongside trees and reused for prediction/eval. +- Package-owned standard scalar trees are validated before prediction. Compact + arrays are detached, permitting builder scratch reuse without sample-sized + copies. Optional cached predictions must match the tree. +- CPU zero-curvature root handling is explicit. The legacy split kernel does + not define 0/0 candidate scores, so the CPU adapter rejects the unverified + reg_lambda=0/min_child_weight=0 pair rather than silently changing it. This + is a declared P3 boundary; future primitive work can broaden it. +- Slice 2: 93 focused/legacy tests passed; production and new-test lint passed. +- Tests hand-check two channels/two rounds, explicit dispatch even when native + eligibility is true, unhalved split gain, cache consistency and zero curvature. + +## Failed Attempts + +- All-zero weights initially passed; fixed at experimental preflight. + +## Risks and Follow-ups + +- A native extension adapter remains future CUDA work; no unverified + adapter is exported by this CPU contract. +- Read-only views prevent accidental mutation; this is not a sandbox against + deliberately hostile Python plugins accessing underlying memory. + +## Commits + +- `4c16204` — completed P2 evidence. + +## Persistence slice + +- Reuse the existing version-2 tree/binner serializer with an experimental + version-1 marker and an explicit inference-state whitelist. Training plugins + and config are excluded; loaded models reject fitting. Missing coefficients + use the saved constant learning rate, while invalid counts/values fail. +- Early stopping snapshots and restores coefficients alongside trees. Last-round + and train-end learning-rate mutations now fail for the experimental path. +- Six initial persistence tests failed because Booster had no save/load API; + checkpoint fell back to pickling the custom objective. All 13 persistence and + callback-boundary tests now pass, including categorical/missing round trips, + nonconstant coefficients, unpicklable plugins and invalid versions. +- Shared legacy version-1 categorical loading currently warns; the new facade + rejects that state without changing legacy policy. +- Regression: 174 passed across experimental objective/dispatch/persistence, + foundation contracts, persistence/unified persistence, growth, formula, + survival and distributional tests. Run with `OPENBOOST_BACKEND=cpu + NUMBA_NUM_THREADS=1 uv run --no-sync pytest -n 0 -q`. + Includes seed replay with plugin and builder RNG consumption and unchanged + global NumPy RNG state. Production/changed-test lint and docs build passed + (existing griffe documentation warnings remain). +- The combined callback run was interrupted during the old 500/1000-round + GBDT early-stopping cases. Faulthandler showed repeated Python sample/tree + prediction through `_fit_cpu -> predict -> _predict_standard_cpu`; reducing + Numba threads did not remove that cost. These three long tests are not claimed + as passed. Focused callback and new coefficient restoration checks substitute + for this slice; no unrelated predictor optimization was made. + +## P3 completion and isolated wheel evidence + +- Focused legacy callbacks: 8 passed / 3 deselected; combined with the 174-case + run, 182 relevant tests passed. The three deselected long early-stopping + tests remain unverified in this slice, not a passing full CPU suite. +- Clean source `f414b8c` built wheel `openboost-1.0.0rc1-py3-none-any.whl`, + SHA256 `5d0560682f7940252c15c9d5f8a67ccbe64849fdd8515aeaf600778c5595439c`. +- Isolated Python 3.12.12 CPU environment: NumPy 2.2.6, Numba 0.61.2, + llvmlite 0.44.0, SciPy 1.16.3, joblib 1.5.3. Actual import came from temporary + uv `site-packages`, with `python -I`; no training fixture module was imported. + Three rounds of nonconstant coefficients reproduced both raw channels exactly. +- Failed installation attempts: offline cache lacked joblib; unpinned uv selected + Python 3.14 / Numba 0.67 / llvmlite 0.49 and failed building against local LLVM + 20 (required 22). Pinning the development Numba 0.63.1 still required an x86 + macOS source build and failed in setuptools with `dry_run`. Public binary + wheels for Numba 0.61.2 succeeded with `--no-build`. No broad fresh-install + compatibility claim follows; dependency/platform packaging remains a follow-up. +- Reproducible harness: `tests/check_experimental_wheel_inference.py` (create + mode imports deliberately unpicklable test plugins; verify mode imports only + NumPy and the installed public OpenBoost API). From the repository, run: + +```sh +OPENBOOST_BACKEND=cpu uv run --no-sync python -m tests.check_experimental_wheel_inference create /tmp/ob-inference +uv build --wheel +OPENBOOST_BACKEND=cpu uv run --isolated --no-project --python 3.12 --no-build \ + --with /absolute/path/to/dist/openboost-1.0.0rc1-py3-none-any.whl \ + --with numba==0.61.2 --with numpy==2.2.6 --with scipy==1.16.3 --with joblib==1.5.3 \ + python -I /absolute/path/to/tests/check_experimental_wheel_inference.py verify /tmp/ob-inference +``` + +- G1 is satisfied within the documented CPU/scalar-tree boundary. GPU extension + primitives and dispatch are P4/P5; independent extension packages are P6. + No external adoption or performance gain has been demonstrated by P3. +- Implementation commits: `50b3631` objective facade, `b2b3a7a` builder/schedule, + `f414b8c` persistence/callback state. Subsequent changes only document evidence + and add the standalone verification harness; library source is unchanged. diff --git a/learnings/2026-09-05-foundation-p4-builder.md b/learnings/2026-09-05-foundation-p4-builder.md new file mode 100644 index 0000000..ef73f25 --- /dev/null +++ b/learnings/2026-09-05-foundation-p4-builder.md @@ -0,0 +1,74 @@ +# 2026-09-05: P4.4 level-wise builder assembly + +## Context + +All three primitive slices have real T4 evidence. Assemble the smallest builder +needed for the Normal/bounded-leaf independent-package experiment, without +adding a second trainer loop to the library or changing legacy model defaults. + +## Decision or Result + +LevelWiseBuilder composes histogram, split, routing and leaf rules over fixed +slots. It is explicit opt-in on the CPU facade and callable directly on CUDA. +The existing CPUHistogramBuilder default is retained because it supports a wider +feature/parameter boundary; the new builder is the candidate experimental GPU +path, not a validated default replacement or speed optimization. + +## Changes + +- Maintain frontier and leaf masks across levels. Route actual samples, retain + early leaves, and apply leaf rules only to the final routed leaves. Release + each histogram before allocating the next; use one declared histogram budget. +- Return standard host TreeStructure plus an owned same-device training cache. + Only five O(nodes) arrays download after CUDA growth; no per-level histogram + or sample-ID download. BuiltTree annotation now admits either array backend. +- Preflight numeric/nonmissing metadata, L2/full sampling, depth/array/device, + default CUDA stream, leaf capability and histogram budget. Validate via the + existing config contract. No implicit fallback or parameter clamping. +- CPU whole-tree oracle scans original row masks recursively, independently + computing splits and final leaves. CPU trainer/persistence exercises two + channels, nonconstant coefficients and bounded versus default leaves. +- GPU suite checks input-view lifetime, compact copy shapes/count, two-round + Normal mean/log-scale composition at 16/4097 rows, optional clipping and CPU + load prediction. GPU Booster.fit remains P5, distinctly unimplemented. + +## Verification + +- Initial collection failed because LevelWiseBuilder was not exported. +- Depth 0/1/2/3 whole-tree reference, early leaf/zero curvature, strict preflight, + two-channel actual CPU trainer and prediction save/load tests passed. +- Focused CPU regression: 120 passed across builder, primitives, evidence runner, + objective, dispatch and persistence tests. Changed production/support lint passed. +- Public Normal adapter example executed with loc/scale channels. MkDocs build + passed (existing griffe docstring warnings). Real-device results follow below. + +## Failed Attempts + +- Lint rejected ambiguous row-mask variable names in the independent oracle; + renamed them without changing the reference computation. + +## Risks and Follow-ups + +- CUDA assembly passed the bounded real T4 suite below; full GPU trainer remains P5. +- Only numeric L2/full sampling/default stream; fixed full slots may cost time. +- Synthetic CPU/CUDA NLL/CRPS agreement is a numerical test, not external quality, + speed or adoption evidence. CPU default is intentionally not broadened/replaced. +- After P4.4, advance independent CPU extension wheel examples before P5, as + agreed in the goal review; record concrete install/API obstacles. + +## Commits + +- `7dfeba4` — P4.3 frozen T4 evidence. + +## Frozen real-device evidence + +- `e02403b` implementation: real T4 6 passed / 0 skipped, pytest 25.22 s. +- [Raw artifact](../benchmarks/results/foundation/20260905T163823Z-7b16b556/README.md) + validates independent whole-tree reference, input-view release/cache lifetime, + 16/4097-row two-channel two-round composition, clipping and CPU save/load. +- Maximum raw CPU/CUDA error 1.7881393432617188e-7; synthetic NLL/CRPS agree. + Named compact copies: 85 calls / 2,380 bytes over 17 trees. No profiler claim. +- Offline evidence validator passed. All 12 upload hashes and lock hash matched + the clean commit; JUnit copies matched; private URL/local-path scan passed. +- Next: independent CPU extension wheel installation and public API conformance + (P6 CPU subset), then P5 strict GPU integration. No adoption claim yet. diff --git a/learnings/2026-09-05-foundation-p4-histograms.md b/learnings/2026-09-05-foundation-p4-histograms.md new file mode 100644 index 0000000..68433e2 --- /dev/null +++ b/learnings/2026-09-05-foundation-p4-histograms.md @@ -0,0 +1,67 @@ +# 2026-09-05: P4.1 fixed-slot histogram primitive + +## Context + +P3 completed the CPU extension contract. P4.1 needs a device-preserving aggregate +rather than the legacy dictionary wrapper that downloads arrays. + +## Decision or Result + +Expose HistogramBatch/build_histograms with separate G/H and row counts. Reuse +sample-centric scatter semantics in a small CuPy RawKernel; CPU uses a compiled +sample loop. Existing native kernels use a coupled (..., 2) layout and no public +active mask/count contract, so they are left unchanged. This is a correctness +primitive, not an end-to-end performance change or a new GPU Booster path. + +## Changes + +- Fixed up to 511 slots, uint8 bins including reserved 255, int32 counts, float32 + G/H, bool active mask, default 256 MiB returned-buffer budget. -1 IDs exclude + rows; inactive nodes ignore them. Zero-weight rows still count; G/H are already + weighted. Invalid IDs/dtypes/devices/values and float32 overflow fail explicitly. +- CUDA supports the current CuPy stream and leaves every aggregate on device. + Validation downloads only scalar booleans; transient validation masks and + caller input buffers are explicitly outside the histogram budget. +- Standalone real-device suite uses the existing pinned image and wheel-only + bundle. Direct sample sums provide independent expected values; no production + histogram constructs expected arrays. Small exact and random/empty cases, + nondefault stream, separate counts, missing bin, budget and negative paths. + +## Verification + +- Initial CPU test collection failed because build_histograms was not exported. +- 74 distinct focused tests passed (73-case regression plus added zero-curvature/ + overflow case): batch histogram, runner, experimental objective/dispatch/ + persistence. Changed-file/production lint and docs build passed; only existing + griffe documentation warnings. GPU evidence remains pending below. +- GPU download spies cover cp.asnumpy, Numba copy_to_host and the legacy wrapper; + they are not a profiler or proof about arbitrary external calls. + +## Failed Attempts + +None beyond the initial red test at the time of implementation commit. + +## Risks and Follow-ups + +- The histogram primitive passed on real T4; downstream GPU tree integration + remains unverified. +- Atomic summation order is nondeterministic. Scalar validations synchronize. +- Split/routing, leaf reduction and LevelWiseBuilder remain P4.2–P4.4. Existing + Booster training remains CPU-only; no downstream GPU split/quality claim. + +## Commits + +- `cfd7dc5` — completed P3 evidence. + +## Real-device result + +- Clean `cf61611` wheel: **3 passed / 0 skipped** on Tesla T4, 19.47 s pytest, + 23.49 s remote function. [Raw artifact](../benchmarks/results/foundation/20260905T150940Z-a5c80f7f/README.md). +- Direct sample oracle: max absolute G error 9.835e-7, H error 1.252e-6, + counts exact. Small weighted/missing and empty fixtures are exact. Nondefault + stream and named host-download/legacy-wrapper blockers passed. Negative H, + invalid IDs, mixed devices and budget rejection passed on CUDA. +- Offline runner accepted the result; uploaded file hashes match the clean + source and JUnit exactly matches the report embedded in results.json. +- P4.1 complete. Next P4.2: exhaustive split oracle and routing from actual rows. + No P4.2–P4.4 or end-to-end experimental CUDA training claim is implied. diff --git a/learnings/2026-09-05-foundation-p4-leaves.md b/learnings/2026-09-05-foundation-p4-leaves.md new file mode 100644 index 0000000..7b27050 --- /dev/null +++ b/learnings/2026-09-05-foundation-p4-leaves.md @@ -0,0 +1,85 @@ +# 2026-09-05: P4.3 leaf reduction and explicit rule + +## Context + +P4.2 established numeric split/routing. Continue the minimum path to the bounded +leaf example and level-wise builder, keeping the goal-review decision to test +independent CPU packages before polishing complete GPU integration. + +## Decision or Result + +Separate real-row reduction from leaf arithmetic. LeafStatistics/reduce_leaves +expose compact G/H/count/active arrays; leaf_values composes the reduction with +an explicitly supplied rule. NewtonLeafRule is the default L2 reference. No +new tree type or alternate split criterion is introduced. + +## Changes + +- CPU compiled row sum and CuPy RawKernel reduction derive all statistics from + routed rows. G/H already include weights; physical counts include zero-weight + rows. -1 IDs and inactive slots are ignored; at most 511 slots. Invalid + arrays/IDs/curvature and reduction overflow fail explicitly. +- Rules declare devices and return same-device contiguous finite float32 + values. A shared ExecutionContext can carry the fit RNG/channel; standalone + calls create a seed-scoped context. Compact G/H copies and output detachment + prevent scratch lifetime/mutation from corrupting model state. G/H mutation + is rejected, including device mutation. No sample-sized protection copy. +- Default L2 rule handles zero/zero as zero, nonzero/zero as failure. Empty, + inactive and zero-statistic leaves must be zero. The default rule rejects L1. +- A bounded rule is defined independently in tests with public API/array calls. + The actual CPU trainer uses it through a root builder; GPU tests compose two + rounds directly. These are distinct scopes, not a claim of a GPU Booster. + +## Verification + +- Initial tests failed collection because leaf_values/reduce_leaves were absent. +- Direct sample sums check nonuniform/zero weights, ignored and empty slots. + Two-round hand oracle: unbounded raw=3.36, clipped raw=1; second weighted + gradients [0.8,-3.6] versus [-1.5,-10.5] for y=[2,4], weights=[1,3]. +- Output dtype/shape/device/finiteness, inactive values, input mutation, scratch + ownership, L1 rejection and zero-curvature/overflow failures are tested. +- Final local and real-device results are recorded below. + +## Failed Attempts + +None beyond initial red collection at implementation time. + +## Risks and Follow-ups + +- The leaf primitive/rule passed on real T4; assembled GPU Booster remains + unverified. +- Atomic float32 reduction order is not deterministic; scalar checks synchronize. +- Default leaf arithmetic and clipped variants use the same existing numeric + L2 split criterion; clipped split optimality is not claimed. +- Next is P4.4 LevelWiseBuilder and independent CPU package usability. GPU + trainer, end-to-end quality/cost and external adoption remain later gates. + +## Commits + +- `383f5cd` — P4.2 evidence and earlier CPU extension usability checkpoint. + +## Local verification + +- `OPENBOOST_BACKEND=cpu NUMBA_NUM_THREADS=1 uv run --no-sync pytest + tests/test_batch_leaves.py tests/test_batch_splits.py tests/test_batch_histograms.py + tests/test_foundation_runner.py tests/test_experimental_objective.py + tests/test_experimental_dispatch.py tests/test_experimental_persistence.py + -n 0 -q`: **107 passed**. +- Production/changed-file lint and docs build passed; existing griffe warnings + remain. GPU results must come from the committed wheel below. + +## Real-device evidence + +- Clean `4da7f4b`: **5 passed / 0 skipped** on Tesla T4. 23.90 s pytest, + 28.17 s remote function. [Raw artifact](../benchmarks/results/foundation/20260905T152639Z-62d96727/README.md). +- Direct row G/H sums and physical counts match exactly for the small, dyadic + random and empty fixtures. Newton/bounded values meet rtol=atol=1e-6. +- Two GPU-composed rounds reproduce raw≈3.36 versus 1 and the independently + calculated second gradients. Actual CPU trainer integration passes separately. +- Nondefault stream, wrong output device/dtype/finiteness, nonzero inactive + output and GPU input mutation checks pass. Scope remains named download + wrappers plus source/device checks, not profiler-level transfer accounting. +- Offline validator accepted results; all uploaded source hashes and the embedded + JUnit were independently matched to the clean source. P4.3 complete. +- Next: P4.4 LevelWiseBuilder; then independent CPU extension wheel examples per + the goal review, before full P5 GPU trainer integration. diff --git a/learnings/2026-09-05-foundation-p4-splits-goal-review.md b/learnings/2026-09-05-foundation-p4-splits-goal-review.md new file mode 100644 index 0000000..5b9810c --- /dev/null +++ b/learnings/2026-09-05-foundation-p4-splits-goal-review.md @@ -0,0 +1,86 @@ +# 2026-09-05: Goal review and P4.2 numeric split/routing + +## Context + +User asked to review the larger goal, then continue. P4.1 established device +histograms, but not usable external GPU algorithms or adoption/value evidence. + +## Decision or Result + +The target remains useful calibration-first distributional/risk modeling plus +an easier route from Python research to a reproducible implementation. Treat +the GPU foundation as a bounded test of that goal, not a product success metric. +G3/G4/G5 remain open: independent GPU extensions, end-to-end engineering value, +and external author use. Continue the shortest path to the Normal objective, +bounded-leaf and channel-schedule examples; avoid new tree families/criteria. +The next product checkpoint is installed public-API extensions and recorded +implementation/installation costs, followed by real author attempts. No outreach +or publication is authorized by this review. + +## Changes + +- Record the goal review and next decision gates in the design document. +- New SplitBatch/find_splits/partition reuse HistogramBatch, return device arrays + and fixed child indices. CPU compiled scan and CUDA kernel use float64 prefix + sums/scoring, strict positive gain and inclusive min_gain/min_child_weight; + exact ties resolve feature then threshold. Inactive/terminal/invalid slots are + explicitly masked. Partition returns owned IDs from actual input rows. +- Numeric L2 only. Positive curvature required in each child even at minimum=0; + histograms have no per-bin physical row counts to distinguish empty bins from + zero-weight bins. This is a visible conservative split-admissibility boundary, + sufficient for the first Normal/leaf experiment, not general Hessian support. +- Hist missing-bin mass is rejected; partition rejects all missing bins. Builder + categorical/missing preflight remains required in P4.4. No default trainer, + persistence or existing split backend was changed. +- The small CUDA split scan prioritizes deterministic reference semantics over + optimized throughput. No speed claim; profiling and end-to-end comparison + are still required before calling the GPU path valuable. + +## Verification + +- Initial collection failed because find_splits/partition did not exist. +- CPU expected split comes from exhaustive direct row masks, not histograms. + Cases include weighted/zero-weight rows, positive/negative/zero gain, exact + ties, gain/child boundaries, no legal split, terminal/inactive/empty nodes, + bad parameters/IDs/children, missing bins and true routed child statistics. +- Final local and real T4 results follow below; no skipped job is a GPU pass. + +## Failed Attempts + +- Initial lint found compact multi-statement test lines; formatted new files. + +## Risks and Follow-ups + +- Numeric split/routing passed on real T4; whole experimental GPU training + remains unverified. +- Positive-curvature children are narrower than arbitrary custom objectives. +- P4.3 leaf rule/reduction and P4.4 builder are next; no complete experimental + GPU trainer, external adoption or matched-quality cost benefit is proved here. + +## Commits + +- `d3cebe6` — P4.1 frozen T4 evidence. + +## Local implementation verification + +- `OPENBOOST_BACKEND=cpu NUMBA_NUM_THREADS=1 uv run --no-sync pytest + tests/test_batch_splits.py tests/test_batch_histograms.py + tests/test_foundation_runner.py tests/test_experimental_objective.py + tests/test_experimental_dispatch.py tests/test_experimental_persistence.py + -n 0 -q`: **90 passed**. +- Production/changed-support lint passed; docs build passed with existing griffe + warnings. No end-to-end GPU extension training was executed locally. + +## Real-device verification and next value checkpoint + +- Clean source `b75b95a`: **4 passed / 0 skipped**, real T4. 20.23 s pytest, + 24.79 s remote function. [Raw artifact](../benchmarks/results/foundation/20260905T151819Z-e5eb30b7/README.md). +- Feature/threshold/IDs matched exhaustive row oracle exactly; gains matched + at rtol=atol=1e-10. Exact ties and gain/child equality, actual child aggregates, + next-layer split topology, invalid routes and missing rejection passed. +- Source file hashes and embedded JUnit independently verified; offline runner + accepted the saved evidence. No runtime/performance claim beyond test duration. +- P4.2 complete. After P4.3/P4.4, advance the CPU portions of P6's independent + packages ahead of full P5 integration to expose usability/installation costs + sooner. GPU gates stay mandatory. External author attempts remain G5, not + something self-authored examples can satisfy. diff --git a/learnings/2026-09-05-foundation-p5-trainer.md b/learnings/2026-09-05-foundation-p5-trainer.md new file mode 100644 index 0000000..ba4f8dc --- /dev/null +++ b/learnings/2026-09-05-foundation-p5-trainer.md @@ -0,0 +1,84 @@ +# 2026-09-05: Strict CUDA extension trainer integration + +## Context + +P4.4 verifies direct GPU builder composition and P6 CPU verifies independent +wheel installation. Actual experimental Booster.fit still ran only on CPU. +Reuse the unified trainer rather than duplicate its round loop. + +## Decision or Result + +Add a strict device extension session to the shared trainer. CPU initialization +and binning are explicit; CuPy owns raw/targets/weights and device updates. +Default CUDA selects LevelWiseBuilder, while explicit builders keep priority. +CPU defaults remain unchanged. Normal/Poisson adapters declare device support +only when the existing objective exact-type capability permits it. + +## Changes + +- Device objective bridge validates dtype/device/finiteness/ownership and input + mutation. Device builder session validates compact standard tree state and + independently traverses it on GPU to verify optional cached predictions. +- CuPy cannot offer NumPy read-only views, so borrowed plugin inputs are isolated + by device copies and checked after the call. This includes binned inputs per + tree and is a known memory/bandwidth cost, not an optimized performance claim. +- Preflight rejects missing/categorical, sampling, L1, eval/callbacks/early stop, + unsupported device/leaf capabilities, budget and non-default stream. Warn + fallback selects the full CPU path; runtime errors use existing rollback. +- Report actual stages, CPU binning/init and scoped transfer/copy behavior. + Model persistence retains host binner/tree state; prediction remains CPU. +- GPU harness exercises default/explicit dispatch, weighted two-channel two-round + schedule, CPU/CUDA raw/NLL/CRPS, CPU load and failure rollback with blocked + legacy dispatch and named host-download wrappers. + +## Verification + +- New CPU preflight tests first failed against the CPU-only facade. +- Focused preflight, evidence runner, objective, dispatch, persistence and builder: + 89 passed. Real T4 validation follows after a clean implementation commit. + +## Failed Attempts + +- Full CPU regression completed during implementation: 904 passed, 34 skipped, + 20 deselected in 325.15 s. No failures. The final targeted preflight/extension + run after the last preflight edits passed 89 tests; the expanded evidence runner + passed 20. Lint and MkDocs build passed (existing docstring warnings). + +## Risks and Follow-ups + +- Real-device core integration and declared adapter modes passed below. +- Wrapper checks are not a profiler trace. Record profiler availability honestly. +- Independent P6 packages still CPU-only; subsequent work must implement/test their + GPU paths as installed wheels. Eval/early stopping remain CPU-only. +- Device defensive copies and independent traversal can be expensive; measure + before any speed/cost/value claim. No external adoption claim. + +## Commits + +- `a9d34f5` — preceding CPU independent wheel evidence. + +## Initial T4 result and coverage expansion + +- `8c34b26` actual trainer suite: 7 passed / 0 skipped; raw max error 1.19e-7, + matched NLL/CRPS and CPU save/load, runtime/input/cache failure rollback. + [Initial artifact](../benchmarks/results/foundation/20260905T175651Z-b1f9743a/README.md). +- Profiler gap confirmed: nsys unavailable. Named transfer checks only. +- Follow-up test coverage verifies all advertised adapter modes (Normal/Poisson, + ordinary/natural), a duck-typed external builder and invalid dtype/Hessian/alias + outputs. This broadens checks without changing the implementation or tolerance. + +## Final declared-surface evidence + +- `4299858`: [final T4 artifact](../benchmarks/results/foundation/20260905T180000Z-7d73ba83/README.md), + 7 passed / 0 skipped; Normal/Poisson × ordinary/natural modes and external + duck-typed builder all pass. Maximum raw CPU/CUDA error 1.19e-7; NLL/CRPS agree. +- Invalid dtype, negative Hessian and output alias checks pass in addition to + runtime, mutation and cached-update failures. Source/lock hashes and JUnit + independently verified; offline validator and privacy scan passed. +- Independent CPU wheel conformance also reran: 5 passed, weighted demo passed, + six exact predictions after plugin uninstall. That local result is marked + dirty because the initial GPU evidence directory appeared during the run; + its wheel hash exactly matches the clean T4 implementation wheel. Do not + present it as a separate clean-source evidence artifact. +- P5 core execution gate passes with the documented profiler gap (nsys absent). + Next: P6 actual installed GPU extensions, then P7 profiling/quality/cost. diff --git a/learnings/2026-09-05-foundation-p6-cpu-wheels.md b/learnings/2026-09-05-foundation-p6-cpu-wheels.md new file mode 100644 index 0000000..21fe3f5 --- /dev/null +++ b/learnings/2026-09-05-foundation-p6-cpu-wheels.md @@ -0,0 +1,79 @@ +# 2026-09-05: Independent CPU extension wheel boundary + +## Context + +The goal review moved the CPU portion of P6 ahead of P5 so we test actual +install/use friction before adding more GPU implementation. P4.4 established +an opt-in level-wise builder; independent method packages were still absent. + +## Decision or Result + +Implement two separately built example wheels with three extension points. +Normal/Fisher math is independently implemented; bounded leaves use the public +Newton rule. No OpenBoost core change or private import is required. Capabilities +remain CPU-only until real GPU package/trainer tests pass. + +## Changes + +- `examples/extensions/normal_fisher`: weighted two-parameter Normal objective, + expected diagonal Fisher and prescribed channel decay. Reject unsupported + extras/non-finite states; do not silently clip raw state. +- `examples/extensions/bounded_leaves`: bounded Newton values during tree growth. + Original split criterion is retained. +- Separate package mathematical tests plus exhaustive original-row depth-one + two-round reference; distinguish objective-only, scheduled, bounded and + combined fits. Check changed second gradients and real early-stop restoration. +- Fresh outside-repo venv installs three wheels without editable/PYTHONPATH; + records installed paths, versions, source/wheel hashes and JUnit. Uninstall + both plugins, restart Python and compare six saved models exactly, including + the executed 64-row weighted public demo. +- Fixed CPU dependency requirements and source-line counts record setup cost. + JSON excludes temporary/user paths; JUnit hostname is removed. + +## Verification + +- Initial package test failed collection with missing normal_fisher, as expected. +- First successful fresh installation: 5 passed, five exact plugin-free model + roundtrips. Public-demo roundtrip and final clean-source artifact follow below. +- Final development run: 5 installed-package tests passed; executed public demo; + six exact CPU model roundtrips after uninstall in a new interpreter. +- Existing extension/builder/leaf regression: 76 passed. Production/example lint + and MkDocs build passed (existing griffe warnings). + +## Failed Attempts + +- Offline install could not resolve pytest from cache. Retried public dependency + downloads through uv into a disposable environment; no source upload. +- Copying development Numba 0.63.1 / llvmlite 0.46.0 pins on Intel macOS triggered + source builds and failed in llvmlite/setuptools with a dry_run argument error. + Pinned Numba 0.61.2 / llvmlite 0.44.0 / NumPy 2.2.6 for the example verification; + these satisfy project ranges. Did not copy the development site-packages or + change the repository's environment/dependency policy to hide installation cost. + +## Risks and Follow-ups + +- CPU self-authored package conformance is not external adoption. External author + work, GPU package conformance, held-out quality and measured value remain open. +- P6 CPU portion only; P5 strict GPU dispatch/residency is the next integration + gate, then run these actual packages on GPU. No GPU capability is declared yet. +- Intel macOS dependency selection is a concrete setup obstacle. The tested pins + establish one installation, not cross-platform/version compatibility. + +## Commits + +- `00ca3b2` — P4.4 frozen T4 builder evidence preceding this slice. + +## Frozen clean-source evidence + +- `3f8addd`: [artifact](../benchmarks/results/foundation/p6-cpu-3f8addd/README.md), + clean-source rebuild, 5 passed / 0 skipped; weighted public demo executed; + six exact CPU roundtrips after both plugins were uninstalled. +- All recorded source hashes matched git objects; JUnit/test counts, absence of + plugins and sanitized paths verified. OpenBoost wheel hash matches P4.4: + no library core changes were needed for these packages. +- Measured method source size (including blanks/docstrings): 89 lines for + objective/schedule, 22 for bounded leaves. Setup requires three wheel builds, + public builder selection and an explicit constrain call for sigma; dependency + selection on Intel macOS was the concrete installation obstacle. +- CPU P6 portion is complete. Next is P5 strict GPU trainer integration, then + extend and test these actual installed packages on real CUDA hardware. diff --git a/learnings/2026-09-05-foundation-p6-gpu-wheels.md b/learnings/2026-09-05-foundation-p6-gpu-wheels.md new file mode 100644 index 0000000..7f8be2e --- /dev/null +++ b/learnings/2026-09-05-foundation-p6-gpu-wheels.md @@ -0,0 +1,91 @@ +# 2026-09-05: Independent CUDA extension wheel boundary + +## Context + +P5 verifies strict GPU training through the built-in adapter and an external +builder. The two actual independent packages still declared CPU only. Complete +the GPU part of P6 without modifying the core trainer or importing private APIs. + +## Decision or Result + +Version 0.2.0 of both example wheels declares CPU/CUDA support, with lazy CuPy +imports and optional CUDA dependency extras. Normal/Fisher formulas are unchanged; +step uses the explicit execution context and rejects host/mixed CUDA inputs. +Initialization stays CPU; loss is an explicit scalar; constrain preserves device. + +## Changes + +- Normal objective dispatches all vector math through NumPy/CuPy. Reject zero + precision caused by extreme scale underflow, as well as non-finite statistics. +- BoundedNewton uses the existing public Newton rule and context-array clipping; + no core edits. Demo accepts --device cpu/cuda and reports actual execution. +- Foundation extensions suite installs all three independently built wheels in + a source-free container. Hashes cover wheels and package source; compare each + installed Python file with the wheel. Test math against independent float64 + NLL/Fisher, then 16/4097-row weighted two-round fits with A, A+C, A+B and A+B+C. +- Inspect clipping effects on leaves and next gradients, schedule effects on + predictions, CPU/CUDA raw/NLL/CRPS and named compact transfers. Execute public + GPU demo in a subprocess; uninstall both packages, restart Python and require + nine exact CPU prediction roundtrips with neither plugin importable. +- Runner rejects missing uninstall/independent-inference evidence even if pytest + passed. CPU wheel conformance remains a separate fresh-venv check. + +## Verification + +- Initial installed CPU wheel run failed both new CUDA capability declarations. +- Updated CPU installation: 7 tests passed plus weighted public demo; six exact + CPU model roundtrips after uninstall. CUDA verification follows a clean commit. +- Related CPU regression: 90 passed. Production/example/harness lint and MkDocs + build passed (existing griffe warnings). + +## Failed Attempts + +- While adding finite-scale negative tests, identified precision underflow to zero + for log_sigma=1000. Reject it explicitly instead of accepting zero curvature. +- Lint required explicit binding of y/weights in the metrics helper; fixed the + test closure before device execution. + +## Risks and Follow-ups + +- Real T4 package conformance passed below; no skipped GPU tests. +- Repository-maintained examples are not third-party adoption. No algorithm + novelty, held-out quality, speed or cost advantage is established here. +- P5 device copies/scalar synchronization and profiler gap remain. Next after + P6 is P7 measured quality/performance/value, with failures retained. + +## Commits + +- `baf029d` — preceding strict CUDA trainer evidence. + +## First real-device attempt + +- `5d10b77`: [failed artifact](../benchmarks/results/foundation/20260905T180943Z-e145df4a/README.md), + 1 failed / 2 passed. GPU finite-difference/Fisher and eight training cells + reached the public demo call successfully, but the subprocess failed CUDA + availability discovery with a multiprocessing traceback. Demo fitting at + module top level lacked a main guard and could re-enter in spawned workers. +- Added a main entry guard, a no-side-effect __mp_main__ import check in the CPU + installer, and full subprocess stderr on failure. Retain original failure; + rerun rather than suppress the CUDA failure or weaken parity tolerances. + +- Entry-guard fix verified locally: 7 fresh-wheel tests and six plugin-free CPU + roundtrips, plus no-side-effect worker import; evidence runner 21 passed. + Preserve partial GPU metrics before launching the demo on future failed runs. + +## Frozen successful evidence + +- `43fcda3`: [passing T4 artifact](../benchmarks/results/foundation/20260905T181351Z-1aee9568/README.md), + 3 passed / 0 skipped. Independent installed GPU math plus eight combination + cells and standalone GPU demo; nine exact CPU predictions after both plugins + were uninstalled and a new interpreter started. +- Maximum raw error 3.58e-7, NLL difference 2.31e-8, CRPS difference 2.86e-8. + These are numerical fixture agreement, not held-out quality or speed evidence. +- Named compact copies: 160 / 4,480 bytes over eight GPU fits; separate demo and + reference transfers excluded. Device copies/scalar synchronization remain. +- Main-guard fix resolved the standalone CUDA discovery failure. Preserve its + original JUnit whitespace verbatim; the commit's whitespace check excluded + only that immutable traceback file, not implementation files. +- All uploaded file, package source, wheel and lock hashes verified; JUnit copies + matched; strengthened offline result gate and private-path scan passed. +- P6/G3 technical package gate passes. Next: P7/G4 matched-quality cost and + developer materials; G5 remains open until an external author's actual use. diff --git a/learnings/2026-09-05-foundation-p7-value.md b/learnings/2026-09-05-foundation-p7-value.md new file mode 100644 index 0000000..1961cd1 --- /dev/null +++ b/learnings/2026-09-05-foundation-p7-value.md @@ -0,0 +1,110 @@ +# 2026-09-05: P7 matched-quality engineering value + +## Context + +P6 proves independent installed CPU/CUDA extensions, not useful speed, cost or +outside adoption. Measure the predeclared P7 tradeoff before broadening claims. + +## Decision or Result + +Freeze the P2 Housing resident configuration/seeds and compare four strategies +in fresh processes on one T4. Pair candidate timings with current legacy CUDA; +use frozen P2 for quality anchoring. Preserve negative value results. Independent +A+B+C changes the math, so it gets a separate quality/cost report. + +## Changes + +- Allowlisted value suite with both installed wheels, frozen data and P2 raw JSON. +- Four timed fits plus separate host profile and sampled device memory per cell. +- Evidence gate rejects incomplete/non-finite/fallback/missing-profile data; + speed and quality failures remain booleans, never silently filtered rows. +- Document cache policy, unsupported eval, sampled-memory limitations and unknown + billing. No core model or training changes. + +## Verification + +- CPU Housing seed 0: all four predictions repeat within tolerance; NLL + 1.094473772, CRPS 0.398590951, coverage90 0.967781008 agree with P2. +- Focused protocol + existing evidence runner tests: 25 passed before final + path-evidence assertions; rerun below before commit. +- Real CUDA value measurements require a clean committed bundle next. + +## Failed Attempts + +- None on real hardware yet. Initial lint caught import ordering; corrected. + +## Risks and Follow-ups + +- cProfile captures host inclusive time, not a CUDA trace. Sampled device-wide + memory is a lower bound, not an exact allocation peak. +- G4 not decided until raw matrix exists; G5 remains open without an outside + author's actual attempt. Three Housing splits cannot establish broad adoption. + +## Commits + +- `15b3b4f` — preceding successful P6 GPU wheel evidence. + +- Final harness verification: 25 focused tests passed; production/harness lint + passed. Standalone CPU worker rerun passed after adding path profiling and + partial-record checkpoints. A missing list bracket in the new profile code + was caught by lint and fixed before any device execution. + +## Developer materials + +- Added a one-page objective/leaf/schedule cookbook and CPU/strict-CUDA capability + matrix; corrected the older guide's CPU-only input/view description for CUDA. +- Added an unsolved independent-author task and observation form. It records + actual time/help/private imports/core edits/GPU evidence and willingness to + depend on OpenBoost. No author has attempted it and nobody was contacted. +- MkDocs build passed with existing griffe warnings. Core and plugin wheel + hashes in the P7 bundle match P6; documentation changes do not alter that code. + +## First T4 value result + +- `eb61218`: [raw matrix](../benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md), + 3 passed / 0 skipped, 12 complete cells. Quality passes; default candidate + warm median 2.078694 s versus legacy CUDA .149556 s: **13.899x**, budget fails. +- Independent A+B+C has worse NLL/CRPS despite coverage closer to nominal. +- Timings are uninstrumented. Separate profiles show sampling-thread calls and + inconsistent inclusive parent/child times; do not use their percentages for + causal attribution. Preserve them and collect isolated host profiles next. +- Sampled memory delta zero is an allocator/context observation, not zero peak + memory. CUDA trace and exact peak remain gaps. No billing dollars inferred. + +## Isolated profiling repair + +- Split host profiling and memory sampling into separate fits. Add explicit + synchronized inclusive objective/builder/session timers; their nested times + overlap and synchronization overhead remains diagnostic-only. +- Add a 600-second, seed-0/four-strategy profile-only suite referencing the + immutable original timing artifact/hash and checking unchanged quality. +- Focused tests: 27 passed. The new isolation test checks that the memory phase + is absent from recorded profiler calls; Python 3.12 cProfile does not expose + its monitoring state through sys.getprofile, so that initial test was replaced + by direct recorded-call evidence. An incomplete-profile-matrix test first + failed and now passes after enforcing exactly four seed-0 cells. +- Standalone CPU profile worker passed; production/harness lint and original + matrix's offline validation passed. No core/plugin modifications. + +## Isolated result and design review + +- `9bb1ff3`: [isolated T4 artifact](../benchmarks/results/foundation/20260905T184856Z-dcd49569/README.md), + 3 passed / 0 skipped; four seed-0 profiles, quality matches parent. No original + timing cells replaced. Core/plugin wheels still match P6 exactly. +- Default diagnostic fit 2.260 s: tree/session boundary 2.123 s, nested builder + 1.588 s, objective boundary .0885 s. Prioritize tree/validation/synchronization + investigation; objective math is not the main measured cost. Do not remove + contracts or infer promised savings from inclusive timers. +- Original performance verdict remains 13.899x, budget failed. No core speed fix + or broader GPU capability expansion is included. Retain bounded research API. +- Developer guide and unsolved author task completed. G5 remains open; no outside + author was contacted. Exact per-fit GPU memory peak and full CUDA trace remain + unverified. Nominal coverage is overconservative; no calibration win claimed. +- P5 CPU regression (904 passed) and P6 CPU/GPU installation evidence apply to + the identical core/plugin wheels. This slice changes only harness/docs; 27 + focused tests, production/changed-file lint and MkDocs passed. Both new GPU + artifacts validate offline. Full hash/JUnit/privacy checks completed below. +- Final audit passed: every uploaded file and wheel hash, parent results hash, + JUnit equality, unchanged executed implementation, private-path scan, and + absence of memGetInfo from isolated host profiles. Final focused rerun: + 27 passed; production/harness lint and both offline evidence gates passed. diff --git a/learnings/2026-09-05-gpu-python-foundation-design.md b/learnings/2026-09-05-gpu-python-foundation-design.md new file mode 100644 index 0000000..e6f0365 --- /dev/null +++ b/learnings/2026-09-05-gpu-python-foundation-design.md @@ -0,0 +1,102 @@ +# 2026-09-05: GPU Python Foundation Design + +## Context + +After the impact/adoption/value research, the user asked whether a GPU Python +boosting foundation was promising, then requested branch confirmation and a +design-first handoff for later implementation with a medium model. Modal use +was explicitly authorized. This turn is planning, not implementation. + +## Decision or Result + +- Created `codex/gpu-python-foundation-design` from clean local `main` at + `82cf1e25b21a69093e85a270af7eb93c9ae7aa19`; local main was not moved. +- Fetched origin/main, now `6ebe3a8ced0e621b17e3cf63e31721af58471053`. + At branch creation the histories had 12 local-only and 9 remote-only commits. + The execution plan starts by merging these histories, preserving both local + correctness fixes and the remote unified trainer/FormulaBoost/WeibullAFT. +- Proposed an experimental, single-GPU, dense-numeric substrate with objective, + tree/leaf, and scheduled coefficient extension points. Reuse the existing + trainer instead of creating another training loop. +- Require CPU mathematical references, real CUDA parity, two separately + installed extension packages, explicit device/fallback reporting, coefficient + persistence, and source-linked raw artifacts. External adoption remains a + separate product gate; self-authored packages do not prove it. +- Keep the repository mission and broad experimental capability limits intact. + +Static inspection found that the extensible primitive path downloads full +histograms/sample node IDs, and `compute_leaf_values_gpu` downloads inputs to +delegate to CPU. The native path bypasses the extension strategy. The design +therefore calls for device batch primitives and explicit dispatch semantics. + +Remote trainer inspection also found a possible mismatch between weighted +Hessians and `const_hess=1` selected using `unit_hessian`. This is a hypothesis +requiring a weighted CUDA regression, not an independently reproduced GPU bug. +Name-based device dispatch and broad exception fallback also need tests. + +## Changes + +- [Architecture design](../planning/gpu-python-foundation-design.md): target + contracts, scope, persistence, evidence thresholds, Modal execution plan, + and conditions under which to stop expanding the foundation. +- [Medium execution checklist](../planning/gpu-python-foundation-execution.md): + P0–P7 tasks, initial failing tests, merge boundaries, verification, and handoff. +- [Learning index](README.md): added this design decision. + +No production code, package configuration, model behavior, or CI was changed. +No actual merge, GPU job, deployment, external message, or push was performed. + +## Verification + +Read the local implementation, tests, public docs, canonical AGENTS and audit, +and the remote trainer/objectives/Modal runner using `git show` at the fetched +revision. A read-only `git merge-tree` inspection identified text conflict +markers in CLAUDE.md and the GPU setup/installation documents; actual merge +resolution remains P0 and may require additional semantic reconciliation. + +Baseline command, run before writing the design: + +```bash +OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/test_extensibility.py -n 0 -q +``` + +Result: **35 passed, 1 skipped in 0.84s**, Python 3.12.12, Intel macOS. +The skipped test requires GPU. This is only the existing extensibility suite, +not validation of the fetched trainer, new design, or CUDA correctness. + +Documentation checks passed for 4 Markdown files, 9 relative links, balanced +fences, 1 Python contract block, and all 7 learning sections. `git diff --check` +passed. Checks use `UV_CACHE_DIR=/tmp/openboost-research-uv-cache` because the +default uv cache is not writable in this sandbox. These files are outside +MkDocs navigation; no production-code regression test was invented for a +reversible design edit. + +## Failed Attempts + +- The initial ahead-only interpretation used stale origin/main information. + Fetch showed both histories diverged. The plan now pins both SHAs and starts + with integration instead of rebuilding functionality already implemented. +- An attempted lookup of remote `tests/test_unified_engine.py` found no such + file. Actual remote model tests are `test_formula.py`, `test_survival.py`, + `test_distributional.py`, and the CUDA verification harness. Do not invent + existing coverage from design-document labels. + +## Risks and Follow-ups + +- No real GPU validation, Modal credential/quota check, timing, or current + billing estimate was performed. Run the bounded smoke after harness work. +- Device-resident custom tree building, safe native dispatch, and model + persistence under nonconstant coefficients are implementation work, not + completed capabilities. +- The suggested 6–8 week window and numeric quality/performance thresholds are + proposed experiment budgets, not measured results or delivery guarantees. +- Contacting external developers has not been authorized; prepare runnable + materials without sending messages. External adoption may remain unverified + after the technical checklist completes. + +## Commits + +- `82cf1e2` — local starting commit, impact/adoption/value research. +- `6ebe3a8` — fetched remote integration target, not merged in this design turn. +- This design and learning entry are committed together; locate the design + commit with `git log -- planning/gpu-python-foundation-design.md`. diff --git a/learnings/2026-09-05-impact-adoption-value-strategy.md b/learnings/2026-09-05-impact-adoption-value-strategy.md new file mode 100644 index 0000000..99121b7 --- /dev/null +++ b/learnings/2026-09-05-impact-adoption-value-strategy.md @@ -0,0 +1,475 @@ +# 2026-09-05: OpenBoost impact, adoption, and value strategy research + +## Context + +Question: where should OpenBoost invest next to improve research impact, actual +adoption, and user value? + +This document records research recommendations, not approved product decisions, +and does not change the AGENTS.md mission or priorities. It assumes a small team +and a 6–12 month horizon, prioritizing real use before amplifying research and +commercial value. Team size, GPU budget, industry relationships, and retention +are unknown. Suggested numbers and deadlines below are experiment gates, not +growth forecasts. + +Research date: 2026-09-05. Evidence is separated into local measurements, online +source/project statements, external primary sources, and untested hypotheses. +No user interviews, willingness-to-pay tests, or new GPU/third-party quality +benchmarks were conducted. + +### Versions that must remain distinct + +- Local checks used `05cd8bc800595a2f40c4d08f51afb697968b9b3e`; local `origin/main` + was `504fdd0bfc60e5d8e7518250e087fb7e4766d1b4`. The initial workspace was clean, + 11 commits ahead of that remote-tracking reference. +- Online `main` read through the GitHub connector already contained FormulaBoost, + WeibullAFT, a unified trainer, and updated probabilistic-modeling positioning. + Returned modification dates were 2026-08-17/18. Those files were absent from + the local snapshot above. +- Online README, old raw.githubusercontent.com search caches, local checkout, + and PyPI entry points disagreed. Research prioritized connector-returned source + and limited local results to the local SHA. No branch was pulled, merged, + reset, or pushed. +- Online sources use mutable `main` links; their immutable SHA was not frozen. + The 26 local test passes cannot verify the newer online architecture. + +Online sources: [README](https://github.com/jxucoder/openboost/blob/main/README.md), +[unified trainer](https://github.com/jxucoder/openboost/blob/main/src/openboost/_trainer.py), +[objectives](https://github.com/jxucoder/openboost/blob/main/src/openboost/_objectives.py). + +## Decision or Result + +Recommended direction: **build a small, complete product around testable +probabilistic predictions; use NaturalBoost to attract adoption, real risk tasks +to demonstrate value, and bounded FormulaBoost experiments to explore research +contributions.** + +A possible eventual positioning: + +> OpenBoost helps Python teams model and evaluate predictive distributions for +> tabular data, incorporating domain formulas when needed. + +“Calibration-first” should describe training, independent calibration, diagnostics, +and deployment verification. `predict_interval` or approximately uniform aggregate +PIT alone cannot promise reliability across all groups, tails, or distribution shifts. + +The immediate question is: **which external team will use OpenBoost again on its +second task, and why?** This tests the direction better than another model class. + +### 1. What each goal optimizes + +| Goal | Desired outcome | Evidence to observe first | +|---|---|---| +| Impact | Others complete previously difficult analyses, methods, or decisions with OpenBoost | Independent reproduction, external integrations, research use, real cases | +| Adoption | External users get started and continue using it | First-success rate, time to useful output, second use, four-week retention | +| Value | Benefits outweigh migration and maintenance | Less compute/engineering time at matched quality, or improved prespecified business decision loss | + +“Independent teams × sustained use × actual improvement per team” can guide +qualitative impact assessment; it is not an estimated growth model. Stars and +downloads indicate reach. Downloads include CI and repeated installation and +cannot directly measure active users. + +### 2. Existing assets and missing evidence + +**Locally checked assets:** NaturalBoost, distribution-parameter predictions, +sample weights, exposure offsets for some distributions, NLL/CRPS/quantile/interval +evaluation, PIT/reliability/recalibration tools, and corresponding tests. There +were 26 focused passes. Historical implementation: +[distributional](https://github.com/jxucoder/openboost/blob/05cd8bc800595a2f40c4d08f51afb697968b9b3e/src/openboost/_models/_distributional.py), +[utils](https://github.com/jxucoder/openboost/blob/05cd8bc800595a2f40c4d08f51afb697968b9b3e/src/openboost/_utils.py); tests: +[distributional tests](../tests/test_distributional.py), [utils tests](../tests/test_utils.py). + +**Existing local third-party comparison:** a CPU NaturalBoost/NGBoost raw JSON +covering three datasets, one seed, and one timing per case showed similar quality +and mixed speed results, insufficient for general superiority. Its metadata does +not cover all current AGENTS.md provenance requirements. +[Raw results](../benchmarks/results/ngboost_comparison_20260720.json). + +**New online capabilities:** FormulaBoost calls formula model → FormulaObjective +→ fit_boosting. Formula loss currently supports MSE only; finite-difference +Jacobians and GGN run on CPU while trees may use GPU. The unified objective has +device paths for Normal/Poisson; the older statement that all distribution +gradients run on CPU cannot be applied to this version. +[FormulaBoost source](https://github.com/jxucoder/openboost/blob/main/src/openboost/_models/_formula.py), +[objectives](https://github.com/jxucoder/openboost/blob/main/src/openboost/_objectives.py). + +**Online experiments are useful leads, not independently verified conclusions +of this study.** The benchmark page reports new GPU timings, 8 UCI datasets, +and synthetic FormulaBoost/Weibull experiments, while acknowledging 3 missing +UCI datasets and no XGBoostLSS/LightGBMLSS comparisons. Its opening calls the +results committed runs, but its ending says JSON is in ignored directories and +tables were transcribed from task records. This study did not verify frozen +raw results and environments corresponding to every table entry, so speed +multipliers are not strategic premises. +[Online benchmark source](https://github.com/jxucoder/openboost/blob/main/docs/benchmarks.md). + +**Adoption entry points remain fragmented.** Local README/quickstart emphasizes +general GBDT; online README emphasizes distributional regression; the default +PyPI page still presents the old stable entry while release history lists +`1.0.0rc1`. Align source, version, installation, docs, and reproducible examples +before a stable release after gates pass. +[PyPI](https://pypi.org/project/openboost/), +[online README](https://github.com/jxucoder/openboost/blob/main/README.md). + +### 3. What competition implies + +| Direction | Alternatives and primary evidence | Implication for OpenBoost | +|---|---|---| +| General distribution prediction | [NGBoost](https://stanfordmlgroup.github.io/ngboost/1-useage.html) offers predictive distributions, proper scores, and survival | Distribution output alone is insufficient to motivate switching | +| Rich distribution families | [XGBoostLSS](https://statmixedml.github.io/XGBoostLSS/) includes distributions, mixtures, flows, and multiple targets | Avoid competing on distribution count; include it on relevant tasks | +| GPU probabilistic regression | [PGBM](https://github.com/elephaint/pgbm) targets large-scale probabilistic regression with GPU support | It is a reasonable task-matched baseline; calling it a reference in our docs does not exclude it | +| Modifiable Python GPU boosting | [Py-Boost](https://github.com/sb-ai-lab/Py-Boost) emphasizes extensibility, multi-output, and GPU | Readable Python needs examples measuring effort saved when adding methods | +| Intervals and risk control | [MAPIE](https://mapie.readthedocs.io/en/stable/) provides conformal/calibration/risk control; [skpro](https://skpro.readthedocs.io/en/stable/) provides probabilistic interfaces, metrics, and pipelines | Favor compatibility/integration over rebuilding a general uncertainty platform | +| Gaussian uncertainty | [CatBoost](https://catboost.ai/docs/en/references/uncertainty) supports uncertainty predictions | Normal comparisons should extend beyond NGBoost | +| New tabular models | [Official TabPFN-3 report](https://priorlabs.ai/technical-reports/tabpfn-3) emphasizes scale, inference efficiency, and calibrated distributions | Do not assume tabular foundation models only handle small samples; independently compare official performance claims | + +These sources establish alternatives, not paying demand. Scenario priorities +below reflect current code fit, accessible data, and verification cost. + +### 4. Choose an initial scenario that can demonstrate value + +**First candidate: model development and validation for insurance frequency/loss.** +Initial users would be experimental actuarial researchers, insurance data +scientists, and risk-modeling consultancies, rather than buyers of a complete +enterprise pricing platform. + +Exposure, nonnegative/count targets, distribution parameters, and tail assessment +fit existing code, with public datasets and mature baselines. The scikit-learn +freMTPL2 example demonstrates Poisson frequency, Gamma severity, and Tweedie pure +premium and can anchor a reviewable comparison. +[Official example](https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html). + +Clarify actual needs. Expected pure premium alone may be adequately served by +GLM/GBDT. Test whether multiple quantiles/threshold probabilities, heterogeneous +dispersion research, or probability quality by exposure reduce engineering work +or improve decisions. A single threshold may be handled by a classifier; do not +assume a full distribution is necessary. + +Start a real case with **Poisson frequency + exposure**, then compare flexible +count distributions. Model and validate severity separately. Local Tweedie +training uses an approximate dispersion gradient and a moment-matched Gamma +approximation for positive quantiles, while `nll()` follows a separate density +path. Interfaces and shape tests cannot validate its tails. +[Distribution implementation](https://github.com/jxucoder/openboost/blob/05cd8bc800595a2f40c4d08f51afb697968b9b3e/src/openboost/_distributions.py). + +Distinguish exposure semantics: current offset checks establish mean scaling +with exposure, not arbitrary Gamma/Tweedie distribution aggregation laws. Define +count, per-claim severity, aggregate loss, annualized loss, and weighting before +comparing dispersion and tails. +[Current exposure implementation](https://github.com/jxucoder/openboost/blob/05cd8bc800595a2f40c4d08f51afb697968b9b3e/src/openboost/_models/_distributional.py). + +| Scenario | Current role | Condition for greater priority | +|---|---|---| +| Insurance frequency/loss | First candidate for real value validation | An external team supplies evaluation goals, runs baselines, and wants repeat use | +| Large-sample regression for NGBoost users | Most direct developer adoption entry | Clear compute or engineering benefit on real data at similar predictive quality | +| Demand/inventory probabilistic prediction | Alternative if insurance users are inaccessible, or later expansion | Partner data and explicit stockout/inventory cost, time splits, and existing forecasting comparisons | +| FormulaBoost structured curves | Bounded research experiment | A real task has credible formulas, sufficiently varying structural inputs, and identifiable parameters | +| Weibull time-to-event | Increase priority with a domain collaborator | Independent real survival data, correct censoring assessment, and strong lifelines/NGBoost baselines | + +Forecasting already has [MLForecast interval workflows](https://nixtlaverse.nixtla.io/mlforecast/docs/how-to-guides/prediction_intervals.html). +For that alternative, provide an integrable regression component instead of +expanding into a complete time-series platform. + +User access may overturn the initial priority: if no useful insurance collaboration +appears within two weeks but a demand/reliability team offers data and time, +follow the verifiable demand. + +### 5. Make adoption a complete workflow + +Minimum stable experience: install → run a public real case → connect user data +→ train/independently calibrate/test → compare baselines → save/reload → reproduce report. + +1. **One default entry.** Choose a target type and provide data to get the first + probability-quality report. Research primitives, other models, and experimental + backends follow later. Build on the online README's existing positioning change + rather than repeating the old audit. +2. **One trustworthy report template.** Include means/quantiles/intervals, + CRPS/NLL, coverage/width, prespecified group/tail checks, model/data versions, + and actual backend. Serve this workflow before building an all-model dashboard. +3. **Separate training and calibration.** Do not use calibration data for final + scoring or uncontrolled repeated tuning. Evaluate distribution quality on an + independent test set. `PITRecalibrator` currently exposes PIT/CDF-level transforms, + not a complete saveable, sampleable predictive distribution with calibrated + quantiles. [Local implementation](https://github.com/jxucoder/openboost/blob/05cd8bc800595a2f40c4d08f51afb697968b9b3e/src/openboost/_utils.py). +4. **A small preproduction boundary.** Verify declared seed, weights, exposure, + missing/categories, early stopping, CPU inference, save/load, and version + compatibility; make fallback explicit. Retain ordinary-CPU trial access. +5. **Use the ecosystem as an entry point.** Improve sklearn compatibility and + NGBoost migration examples first, then choose skpro or MAPIE integration based + on demand. Maintainers determine upstream acceptance; a PR does not guarantee traffic. + +“Useful report in 15 minutes” is a design target to measure with new users, not +an established result. Do not make extra run counts, complex parameters, or +unnecessary dependencies concepts users must understand. + +### 6. Research impact: what structure contributes + +FormulaBoost merits bounded investment because it combines a domain-given +`f(theta(z), x)` with nonparametric parameter heterogeneity. Ask: **under which +data support, formula misspecification, and parameter coupling conditions does +it improve structural-input extrapolation, parameter recovery, or decisions?** + +Do not claim varying coefficients or full natural gradients as inherently novel. +Existing [tree boosted varying coefficient research](https://arxiv.org/abs/1904.01058), +[gamboostLSS](https://search.r-project.org/CRAN/refmans/gamboostLSS/html/mboostLSS.html), +and NGBoost natural gradients motivate further literature and comparison work; +they do not establish that this study completed a novelty review. + +Corrections relevant to publication and positioning: + +- Online survival docs say NGBoost lacks censored likelihood, contradicting its + [official survival docs](https://stanfordmlgroup.github.io/ngboost/1-useage.html#survival-regression). + Online README also calls NGBoost a fixed catalogue, but its + [developer guide](https://stanfordmlgroup.github.io/ngboost/5-dev.html) allows new + distributions and scores. Correct these before promotion. +- Covariate-dependent Weibull shape is not unique across the ecosystem. + [lifelines ancillary regression](https://lifelines.readthedocs.io/en/latest/Survival%20Regression.html#modeling-ancillary-parameters) + supports shape modeling and warns it generally loses standard AFT form. Verify + nonlinear parameter surfaces, computation, and usability rather than unconditional uniqueness. +- XGBoost custom-objective Hessian inputs have diagonal-structure restrictions, + but official docs discuss alternative curvature and approximations. Lack of + full-matrix input does not prove every outer algorithm cannot use coupled + directions. [Official explanation](https://xgboost.readthedocs.io/en/stable/tutorials/advanced_custom_obj.html). +- Online FormulaBoost tables show similar prediction/extrapolation errors for + `diag` and `full`; the main full-matrix lead concerns parameter recovery. + Synthetic cases cannot establish that real tasks require full GGN. + [Report](https://github.com/jxucoder/openboost/blob/main/docs/benchmarks.md). + +For the current scalar-MSE FormulaObjective, a direct derivation applies. For +single-sample Jacobian vector `j`, residual `r`, and positive damping `lambda`: + +```text +G = j j^T +g = r j +(G + lambda I)^(-1) g = r j / (lambda + ||j||^2) +``` + +Thus the current per-sample full-GGN direction reduces to sample-level gradient +scaling. It differs from component-wise `diag` scaling, but this form does not +require general dense matrix inversion. Independent numerical checks at K=2/5 +matched dense solves with maximum absolute errors `2.78e-16` / `5.00e-16`. +This concerns only the present scalar-MSE, positive-damping, per-sample mathematics, +not complete training equivalence, multi-output residuals, aggregated curvature, +or arbitrary numerical safeguards. +[Corresponding source](https://github.com/jxucoder/openboost/blob/main/src/openboost/_objectives.py). + +Compare global, diag, full, and gradient-scaling versions of the same formula +with matched initialization/tuning, sensible structured alternatives, and +formula-free baselines. Include misspecified formulas, weak identification, +insufficient structural-input variation, reparameterization, and different noise. +A single scalar-response Jacobian has rank at most one; matrix preconditioning +cannot invent missing identification information. + +If results are positive, produce a report on when structured boosting helps, +with independently runnable experiments. Consider software publication after +external use and software quality accumulate. [JOSS requirements](https://joss.readthedocs.io/en/latest/submitting.html) +provide review criteria; publication does not replace adoption evidence. If the +user prioritizes publication impact, increase this investment while narrowing +to one clear research question. + +### 7. Evidence design: answer why users would switch + +Keep three questions and their conclusions separate: + +| Question | Experiment | Supported conclusion | +|---|---|---| +| Is general probabilistic quality reliable? | Complete the official ScoringBench protocol using its public comparison workflow | Quality within that protocol and complete dataset collection | +| Is scaling worthwhile? | At least three real datasets, multiple scales/repeats, CPU/CUDA and strong baselines | Cost/scale benefit on declared hardware at the quality threshold | +| Why adopt? | One real domain case plus external reproduction/reuse | Workflow-specific engineering/decision benefits and migration cost | + +[ScoringBench](https://github.com/jonaslandsgesell/ScoringBench) is a suitable +independent quality entry; local [adapters and two protocols](../benchmarks/scoringbench/README.md) +already exist. Label official quality and larger-sample extension separately. +Benchmark completion should not block starting user interviews. + +Compare relevant NGBoost, XGBoostLSS/LightGBMLSS, PGBM, CatBoost uncertainty, or +quantile + conformal. Include currently accessible TabPFN if resources permit. +Filter by target/distribution support before staged narrowing, recording +unsupported cases, failures, and budget differences. + +Prespecify primary metrics, noninferiority bounds, splits, tuning budgets, and +business decisions. Report paired differences and uncertainty intervals. +`p > 0.05` does not establish equivalence/noninferiority. Calibration improvement +also needs width, proper scores, and downstream loss; infinitely wide intervals +cannot constitute a useful calibration win. + +New GPU results require exact source SHA/dirty state, dataset/version/hash, +split/seed, OS/CPU/RAM/threads, GPU/driver/CUDA, dependencies, full commands, +cold/warm policy, and actual backend/fallback. Measure end-to-end fit, prediction, +memory, and transfers. GPU OpenBoost versus CPU NGBoost comparisons must report +resources/cost and include relevant GPU alternatives. Synthetic data tests +mechanisms; real data establishes scenario evidence. + +Survival assessment must account for censoring. Unknown complete event times +cannot supply ordinary real-data coverage. Choose censored NLL and applicable +IPCW/Brier/calibration methods under stated identification and independent-censoring +assumptions. FormulaBoost scenario extrapolation is not automatically causal; +observational curves alone do not justify interpreting price/dose changes as interventions. + +### 8. A 90-day execution plan + +| Time | Main work | Deliverables | Suggested decision gate | +|---|---|---|---| +| Days 1–14 | Reconcile local/online state, correctness/evidence gaps, prepare a domain walkthrough, conduct discovery | Version/capability table, runnable case, interview outline, baseline protocol | Contact about 10 suitable people; at least 3 offer a real evaluation task or one comparison | +| Days 15–30 | Complete third-party quality runs and real Poisson/exposure baseline; observe onboarding | Raw results, failure list, installation-to-report observations | At least 2 external users succeed independently; identify the 3 most common obstacles | +| Days 31–60 | Improve workflow based on obstacles; freeze CPU/CUDA parity/scaling; bounded FormulaBoost falsification | Reusable core, real case study, structured-method comparisons | A domain benefit covers migration cost, or stop that scenario; continue research only with evidence | +| Days 61–90 | Prepare stable release, one ecosystem integration, public technical case, partner pilot according to results | Versioned tutorials/reports, upstream submission materials, reusable onboarding | 5 external teams have used real data; at least 3 continue after four weeks or on a second task; 1 independent reproduction/integration | + +Outreach, upstream submissions, and releases above are future recommendations; +none occurred in this study. Upstream acceptance timing is outside our control; +distinguish completed submission materials from acceptance. + +Suggested small-team allocation: roughly 60% core reliability/complete experience, +25% external use/real evidence, 15% one FormulaBoost research experiment. +Data/compute work may interleave within one person's schedule; this does not +call for launching three products. + +Every new feature must address an external obstacle or a prespecified research +hypothesis. GAM/DART/linear leaves, Ray/multi-GPU/out-of-core/train-many receive +no independent product investment until the main evidence gate passes and +concrete demand exists. + +### 9. Acquiring users and commercial value + +Start with three materials: a real probabilistic-modeling walkthrough, a +reproducible performance/quality comparison, and an NGBoost migration guide. +Use problem-centered titles, such as “Exposure, predictive distributions, and +independent calibration assessment in one Python workflow.” Link every number +to raw results. + +Prioritize authors/maintainers of existing NGBoost projects, count/loss research +teams, and risk consultancies offering validation tasks. Offer concrete paired +experiments and reproduction help; record why users leave or continue. Broad +launch traffic is not the first objective. + +Interview about actual work: how was the most recent probabilistic prediction +completed; which step cost the most time or trust; which decision used its outputs; +what alternatives are used; what result would justify switching; can the team +compare on its own data within two weeks? Verbal agreement is weaker evidence +than data, engineering time, and repeated use. + +Validate commercialization in order: + +1. Auditable benchmark/migration/calibration services to test payment for shorter delivery. +2. If multiple teams repeatedly request them, consider maintenance support, + private deployment, and reproducible versions/reports. +3. Evaluate hosted training/evaluation only after clear recurring paid demand. + +Estimate value as “engineering time saved + compute cost change + verified +decision improvement − integration/maintenance cost.” Do not mechanically convert +CRPS percentages into revenue or invent TAM, pricing, or ARR without interviews +and costs. + +Keep the open-source core easy to use. Build trust by solving shared problems, +then determine which services merit payment; hypothetical commercialization +should not introduce premature friction. + +### 10. Conditions that would overturn the direction + +| Observation | Action | +|---|---| +| Similar general quality but no compute/engineering benefit | Narrow to differentiated domain/custom objectives; stop general-superiority claims | +| Users only need intervals and existing models + MAPIE/skpro suffice | Integrate lightly or acknowledge the better alternative; do not expand into general UQ | +| Users want reports but consistently refuse trainer replacement | Test independent evaluation demand with a small adapter before changing product focus | +| GPU lacks real end-to-end economic benefit | Make GPU optional, optimize from profiling, and pause multi-GPU expansion | +| FormulaBoost only works on synthetic data generated by an exactly correct formula | Retain research examples and pause general extrapolation claims | +| Full-GGN benefits disappear after matched formula/initialization/tuning | Attribute contribution to structure/usability, not matrix form as independent innovation | +| Repeated onboarding rounds produce no second use | Investigate exits and change scenario/experience, rather than adding model types | +| Multiple teams repeat use and pay for support | Increase maintenance/integration for that scenario before discussing a larger product | + +## Changes + +- Added this study to preserve sources, version differences, recommendations, + experiment gates, and unverified hypotheses. +- Added an entry to the learning index. +- Did not modify models, runtime code, public positioning, or benchmark implementation. + +## Verification + +### Focused local behavior checks + +```bash +OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache \ + uv run --no-sync pytest tests/test_distributional.py tests/test_utils.py \ + -n 0 -q -k 'exposure or PIT or ReliabilityDiagram or ProbabilisticMetricsWithOpenBoost' +``` + +Result: `26 passed, 136 deselected in 7.21s`, Darwin, Python 3.12.12. This verifies +selected local behavior, not full regression, the online architecture, or CUDA. + +### Local reproducibility finding + +The independent-process experiment below fixes data and changes only the global +NumPy seed. Local NaturalBoost has no `random_state` constructor parameter and +tree row subsampling uses global `np.random.choice`. Maximum prediction change: +`0.1888485550880432`. This reproduces a local randomness-control gap; it does not +establish whether the online version has fixed it. + +```python +import inspect +import numpy as np +from openboost import NaturalBoostNormal + +rng = np.random.default_rng(7) +X = rng.normal(size=(240, 4)).astype(np.float32) +y = (X[:, 0] + 0.4 * rng.normal(size=240)).astype(np.float32) +preds = [] +for global_seed in (1, 2): + np.random.seed(global_seed) # Deliberately probe reliance on global state. + model = NaturalBoostNormal(n_trees=8, max_depth=2, subsample=0.7) + model.fit(X, y) + preds.append(model.predict(X)) +print('random_state' in inspect.signature(type(model)).parameters) +print(float(np.max(np.abs(preds[0] - preds[1])))) +``` + +### FormulaObjective mathematical check + +Continue from the preceding `rng` state to check the scalar-MSE rank-one identity; +this does not execute complete training of the online model: + +```python +for k in (2, 5): + J = rng.normal(size=(100, k)) + residual = rng.normal(size=100) + damp = 1.0 + matrices = J[:, :, None] * J[:, None, :] + damp * np.eye(k)[None, :, :] + g = residual[:, None] * J + full = np.linalg.solve(matrices, g[..., None])[..., 0] + simplified = g / (damp + np.sum(J * J, axis=1))[:, None] + print(k, float(np.max(np.abs(full - simplified)))) +``` + +Results: K=2 `2.7755575615628914e-16`; K=5 `4.996003610813204e-16`. +This is an algebra check, not a quality or performance benchmark. + +Historical documentation checks: 9 relative file links existed, 2 Python examples +compiled, all 7 template sections were present, code fences balanced, and +`git diff --check` passed. This document is outside MkDocs navigation and changed +no production code; documentation checks were not reported as model regression. + +## Failed Attempts + +- Search-index README, raw files, and commit history disagreed. Switched to + GitHub connector source reads and separately recorded the local tested SHA. +- `git ls-remote` failed because the local environment could not resolve GitHub. + Used the available connector without changing network settings or repository state. +- Could not confirm full raw provenance from newer transcribed benchmark tables; + did not repeat their GPU numbers as verified claims. + +## Risks and Follow-ups + +- User demand and retention are the largest unknowns, not the number of candidate + models. Prioritize externally supplied evaluable tasks. +- Reconcile online and local code before fixes and preserve unpushed commits. +- Initial engineering checks should cover seed control, probability/calibration + consistency, Tweedie approximation limits, persistence, and real CUDA parity. +- This study does not claim established FormulaBoost novelty, ScoringBench + acceptance, independently verified GPU performance, or a verified paying market. +- If research publication or short-term revenue becomes primary, adjust experiment + order instead of mechanically retaining the suggested allocation. + +## Commits + +- This document and the learning index form an independent documentation commit; + Git history records its SHA. diff --git a/learnings/2026-09-05-impact-review-scoringbench-config.md b/learnings/2026-09-05-impact-review-scoringbench-config.md new file mode 100644 index 0000000..08e85f2 --- /dev/null +++ b/learnings/2026-09-05-impact-review-scoringbench-config.md @@ -0,0 +1,62 @@ +# 2026-09-05: Refocus value evidence and repair benchmark configuration + +## Context + +The user asked to review the bigger impact/adoption/value goal and continue. +The latest small GPU optimization leaves a 12.888x experimental/legacy fit ratio. +Continuing internal optimization alone does not validate a useful product. + +## Decision or Result + +Pause general GPU foundation optimization as the immediate queue. Prioritize +trustworthy external quality evaluation and independent-author use, then a real +exposure-aware risk case. Preserve the experimental API and all negative data. +See [next investment gate](../planning/impact-adoption-value-next.md). + +## Changes + +- ScoringBench CLI seed now reaches OpenBoost and XGBLSS; NGBoost receives depth + and seed through a fresh clone of its installed default tree learner per factory. + Previously OpenBoost/XGBLSS omitted model seeds, and NGBoost ignored CLI depth. +- Provenance records constructed wrapper/base parameters, excluding private state, + so the manifest can be audited beyond CLI intent. This is configuration evidence, + not execution-device or benchmark-completion proof. +- Tests isolate optional upstream imports but exercise real OpenBoost fit/predict + with row sampling; no heavy PyTorch import on unsupported Intel macOS. + +## Verification + +- Initial two tests failed on missing OpenBoost model_params and NGBoost Base. +- Four focused tests pass: all constructor seed/depth forwarding, independent + cloned base learners, JSON configuration, and actual seeded OpenBoost fitting. + Same model seed survives changed global NumPy state; changing the seed changes + sampled predictions. Global test RNG state is restored. +- Inspected clean local ScoringBench a938a667b7839b41e9272929010573410301c0b4. +- NGBoost [default learner source](https://raw.githubusercontent.com/stanfordmlgroup/ngboost/master/ngboost/learners.py) + confirms a clonable sklearn tree; clone the installed learner rather than + guessing or hard-coding its other defaults. No upstream repository modified. + +## Failed Attempts + +- No complete Linux run attempted in this slice. Local constructor doubles do + not establish actual competitor fitting or external metric integration. + +## Risks and Follow-ups + +- Audit cached result reuse against config/source and account for failed folds + before treating a full suite as complete. The upstream runner catches dataset + failures and reuses existing result files; a manifest alone is insufficient. +- Next is a bounded, pinned Linux smoke, then a preregistered real quality shard + and full suite. No quality ranking/acceptance/adoption claim is made here. +- Author materials exist, but no outside attempt/contact occurred. No push, + publishing, leaderboard submission or external messaging was performed. + +## Commits + +- `00dd67a` — previous fixed-slot performance evidence, budget still failed. + +- Final verification: 5 focused tests plus 47 distributional regressions = + 52 passed; the additional test verifies constructed configuration is actually + persisted in the manifest. Production/changed-file lint and MkDocs build pass + (existing deprecation/griffe warnings). Full Linux/competitor metrics remain + explicitly unverified; no benchmark-quality result was produced this slice. diff --git a/learnings/2026-09-05-openboost-v1-plan.md b/learnings/2026-09-05-openboost-v1-plan.md new file mode 100644 index 0000000..2754d13 --- /dev/null +++ b/learnings/2026-09-05-openboost-v1-plan.md @@ -0,0 +1,107 @@ +# 2026-09-05: OpenBoost v1 scope, current releases and evaluation + +## Context + +The user designates this planning round as the real OpenBoost v1 and asks for +clear execution, acceptance and evaluation criteria. Insurance and survival/AFT +were examples of real applications, explicitly not a closed application list. +The user also requires review of the latest XGBoost, CatBoost and LightGBM +releases and public plans. Foundation-first and no backward compatibility remain +the governing decisions. +The user then rejected describing insurance/AFT as special important cases: +all listed use cases are needed. This entry and the active protocol include that +correction; the initial planning state is preserved in commit `a9fde60`. + +## Decision or Result + +Define v1 around the cost of a verified algorithm change. Ordinary Python +recipes own algorithm decisions; composable bulk operations and explicit state +support CPU reference execution and a declared CUDA subset. R1–R9 and C1–C7 +specify required recipes and shared capabilities; A1–A13 make every application +an individually required implementation and evaluation obligation. No special +priority attaches to insurance/AFT. Datasets can be selected, but use cases +cannot be replaced by a count of representative successes. + +The reviewed latest releases are XGBoost 3.4.1, CatBoost 1.2.10 and LightGBM +4.7.0. XGBoost's expanded experimental vector-leaf hist support, CatBoost's +existing GPU custom objectives and LightGBM's new GPU/data interoperability +capabilities invalidate a differentiation story based only on those features. +Distinguish shipped capabilities, roadmaps, maintainer intentions and requests; +no unified, dated CatBoost product roadmap was established by this review. + +E0–E6 define engineering completion; E7 separately tests outside adoption. +Numerical criteria are proposed requirements, not measured results: independent +correctness, real-task evidence for every A1–A13 item from at least six sources, fair tuned quality, +end-to-end GPU/train-many/inference cost, five Agent modification tasks plus two +held-out tasks, and clean-environment delivery. Failed or missing required cases +cannot pass. Freeze resource/data manifests before full evaluation; retain old +protocols and failures if requirements are later revised. Protocol `v1-plan-r2` +replaces the previous eight-task coverage gate. Poisson, Gamma and Tweedie have +separate application checks; distributional prediction, a real Formula task and +the train-many model-selection workflow are all required. + +## Changes + +- [v1 plan](../planning/agent-boosting-foundation-plan.md): required scope, + architecture decisions, F0–F5 dependencies, independently reviewable slices + and executor handoff. F0/F1/F4/F5 trace every A-ID; discovery trials can start + after the small CPU path. Implementation order follows component dependencies. +- [Eval protocol](../planning/openboost-v1-evaluation.md): explicit gates, + baselines, budgets, tolerances, failure accounting and result artifacts. +- [Application contracts](../planning/foundation-application-contracts.md): + individually required A1–A13 matrix with recipe mappings, per-use-case checks + and dataset choices; separate conditional means from predictive distributions. +- [Release and plan review](../planning/boosting-release-review-2026-09-05.md): + primary-source snapshot, status distinctions and concrete design implications. +- [Agent guide](../AGENTS.md) and learning index: durable v1 direction and links. + +## Verification + +- Read official release pages and relevant roadmap/issues, objective/device + documentation and primary dataset references. Sources are linked in the + planning artifacts. New competitor versions were not installed or benchmarked. +- Read current WeibullAFT fit/objective, censoring tests and distributional + exposure call sites/tests. Current event-indicator survival behavior does not + establish interval-label support or fixed-noise AFT semantics when shape varies. +- Documentation validation with + `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python`: + initial slice and scope correction both passed all 7 Markdown files, 36 local + links and balanced code fences. Scope-correction checks also passed: A1–A13 + are complete and unique, all R1–R9 recipes are mapped, required coverage + references agree and the protocol is r2. `git diff --check` passed; the staged + diff is inspected before commit. +- No runtime implementation, data download, Modal job, external contact or + publication occurred. Model regression tests do not validate a planning-only + change and were not run. + +## Failed Attempts + +- An insurance/survival-only amendment would overfit the latest examples. The + user's clarification led to separate structural and application coverage axes. +- The first broad plan still allowed eight representative task results and + synthetic-only Formula evidence to satisfy engineering coverage. The user + rejected that narrowing. Every named use case now has a required evidence + obligation; a missing real Formula task or omitted Gamma/Tweedie evaluation + leaves v1 incomplete. +- GitHub API and some filtered pages were unavailable; successfully read release + and specific issue pages support the snapshot, not exhaustive roadmap coverage. +- No new algorithm experiments. The prior P7 T4 ratio of 12.888 versus its 1.2 + budget remains a failure, not rewritten as a pass or a universal disproof of + programmable boosting. See the main plan's original artifact link. + +## Risks and Follow-ups + +- Scope is a full v1 target, not permission to build every subsystem at once. + Start F0.1 task cards, F0.2 independent oracles and F0.3 runnable/frozen protocol; + then use small CPU paths and discovery trials to challenge the abstractions. +- Candidate data, licensing, preprocessing, actual build support and all new + quality/cost/adoption claims remain unverified. Numeric gates are engineering + choices, not statistically demonstrated universal parity guarantees. +- A new algorithm can be mathematically correct and predict worse. Internal + plugins and Agent success do not demonstrate outside authors or adoption. + +## Commits + +- `f30c2ed` — preceding clean foundation design and execution plan. +- `a9fde60` — initial v1 scope and evaluation gates. +- This correction: `docs: require every v1 use case individually`. diff --git a/learnings/2026-09-05-retire-legacy-production.md b/learnings/2026-09-05-retire-legacy-production.md new file mode 100644 index 0000000..6c21f5e --- /dev/null +++ b/learnings/2026-09-05-retire-legacy-production.md @@ -0,0 +1,57 @@ +# 2026-09-05: Retire the old production implementation for a clean v1 rebuild + +## Context + +During Sprint 001 the user asked about deleting everything and clarified the +scope as old production code, rebuilding v1. This overrides the earlier option +to keep old sources until later F1/F5 cleanup. + +## Decision or Result + +Retire the entire old package implementation at a single Git boundary. Keep an +explicitly unfinished v1 namespace, independent references, historical mathematical +tests and raw experiment evidence. Historical reproduction uses `50acfc6`. +This is a clean implementation start, not a completed foundation or feature release. + +## Changes + +- [Sprint 002](../v1-sprints/002-retire-legacy-production.md) is the authoritative + execution/verification/reflection record, including the user-directed sequence change. +- Remove old models, trainer, CPU/CUDA backends, core, experimental and distributed + modules. Remove stale bytecode/JIT caches; no compatibility layer remains. +- Rebuild default test/docs/CI entry points around current v1 work; retained + historical tests are excluded, not relabeled as passing/skipped coverage. +- Set package metadata to `1.0.0.dev0`, drop retired runtime/autodiff/distributed + dependency surfaces and refresh the lock. No package is published. +- README and current docs explicitly state there is no training API. Old GPU and + publishing jobs are unavailable until their v1 gates have real implementations. + +## Verification + +- 55 reference tests pass through the default root test command, without skips; + changed production/test support lint, lock check, strict docs and offline + sdist/wheel builds pass. Isolated wheel import and archive inspection confirm + no old modules remain. Full commands and boundaries are in Sprint 002. +- Four local workflow schemas, 14 Markdown files/76 local links and whitespace + checks pass. No benchmark artifact or committed reference was changed by cleanup. +- Remote CI, CUDA and production model gates were not run; the namespace has no + training API. Historical tests excluded from discovery are not v1 passes. + +## Failed Attempts + +- Offline lock regeneration could not resolve uncached cross-Python metadata. + A normal registry-backed uv lock was needed; this is an environment limitation, + not an excuse to hand-edit dependency hashes or leave a stale lock. + +## Risks and Follow-ups + +- The checkout intentionally cannot train until new public components arrive. + Historical commands require the recorded revision. New component tests must + recover relevant correctness cases without preserving obsolete API contracts. +- Return to remaining F0.2 references, then F0.3 and F1. All application and + quantitative gates remain required; cleanup is not evidence of correctness or speed. + +## Commits + +- `50acfc6` — old production implementation plus independent v1 references. +- This slice: `refactor: retire legacy production code for the v1 rebuild`. diff --git a/learnings/2026-09-05-v1-author-mutation-reference.md b/learnings/2026-09-05-v1-author-mutation-reference.md new file mode 100644 index 0000000..e7b33a2 --- /dev/null +++ b/learnings/2026-09-05-v1-author-mutation-reference.md @@ -0,0 +1,44 @@ +# 2026-09-05: Exact expectile, penalized leaf and ordered acceptance references + +## Context + +The F0.2 acceptance ledger identified missing D1/D3 formulas and D4's exact +six-trial acceptance schedule. These are development task oracles, not E5 results. + +## Decision or Result + +Enumerate expectile stationary intervals and penalized-pinball breakpoints plus +stationary points. Keep penalty normalization explicit: scaling row mass without +scaling the penalty changes the optimum. Ordered updates consume accepted state. + +## Changes + +- [Sprint 008](../v1-sprints/008-author-mutation-reference.md) records exact + mathematics, two-round fixtures, verification and three-commit reflection. +- Add expectile objective/base, penalized residual leaf/tree and D4 ordered rule. +- 26 tests plus isolated production-import checks; update the acceptance ledger. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 255 passed, no skipped; local macOS/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Hand values, smooth-region finite differences, nonsmooth subgradient bounds, + weight replication/scaling, routed two-round leaves and six-trial rejection. + +## Failed Attempts + +- Tests initially failed collection because the reference module did not exist. + The first implemented mathematical batch passed; no thresholds were relaxed. + +## Risks and Follow-ups + +- No public extension package, real author-cost comparison, persistence or GPU was + tested. Full best-state and per-run RNG integration is not proved by local raw + snapshots or checking that this deterministic rule leaves global RNG unchanged. +- Complete the remaining categorical/vector growth and finite integration probes, + audit F0.2 and then freeze F0.3. Do not expand reference work indefinitely. + +## Commits + +- This slice: `test: add exact author mutation references for v1`. diff --git a/learnings/2026-09-05-v1-data-classification-reference.md b/learnings/2026-09-05-v1-data-classification-reference.md new file mode 100644 index 0000000..660a147 --- /dev/null +++ b/learnings/2026-09-05-v1-data-classification-reference.md @@ -0,0 +1,51 @@ +# 2026-09-05: Independent data and classification references + +## Context + +Following the production reset, F0.2 needs train-only transformations and A2/A3 +classification geometry before public foundation components can be checked. + +## Decision or Result + +Keep immutable column transforms and class schemas distinct from tree geometry. +Numeric cuts follow direct linear order statistics; categorical candidates use +equality. Binary loss uses signed margins to avoid cancellation. Softmax exposes +its exact Hessian separately from the diagonal upper bound used for tree fitting. + +## Changes + +- [Sprint 003](../v1-sprints/003-data-classification-reference.md) records scope, + hand fixtures, commands, results and reflection. +- Add independent data/classification reference modules and40 tests; extend the + subprocess check that blocks all production imports. +- Keep full row identity/binding, categorical growth, persistence and production + components explicitly pending. No API or performance claim is added. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 95 passed, no skipped, local macOS CPU/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Independent checks: hand cuts/category gain, finite differences, integer-weight + replication, class permutations, two-round leaves/raw and isolated imports. +- CUDA, external baseline quality and formal E-gates were not run. + +## Failed Attempts + +- Initial tests failed collection on the two absent reference modules, confirming + that this slice required new implementations. No production workaround was used. +- Code review identified clipping smaller than float64 can represent below one; + reject that configuration explicitly to prevent an infinite binary base. + +## Risks and Follow-ups + +- These are tiny NumPy oracles, not optimized backends or public data containers. + Constructors describe fixtures; public schema construction/serialization needs + full validation when implemented. Supported category tokens are homogeneous + strings or integers; other token types are rejected. +- Continue ranking/quantile/vector reference work, then remaining F0.2 and F0.3. + Full v1 use-case scope and all production/evaluation obligations remain intact. + +## Commits + +- This slice: `test: add independent data and classification references for v1`. diff --git a/learnings/2026-09-05-v1-identity-runs-reference.md b/learnings/2026-09-05-v1-identity-runs-reference.md new file mode 100644 index 0000000..1802480 --- /dev/null +++ b/learnings/2026-09-05-v1-identity-runs-reference.md @@ -0,0 +1,46 @@ +# 2026-09-05: Prepared identity, isolated runs and comparable selection + +## Context + +F0.2 A13/D5 needs concrete run isolation, best-state and stable random-key evidence; +C1 requires content/row identity instead of object ID or shape. + +## Decision or Result + +Preserve prepared row IDs alongside the content digest so bind can reject a caller +that consistently misorders all roles. Run heterogeneity does not justify comparing +incomparable validation losses: select only within one problem identity. + +## Changes + +- [Sprint 007](../v1-sprints/007-identity-runs-reference.md) records the bounded + implementation, fixed key, results and reflection. +- Typed hash/bind oracle, real tiny scalar/vector squared-tree runs, immutable best + terms, independent stopping/failure and stable per-run sampling. +- [F0.2 acceptance ledger](../v1-sprints/f0-2-acceptance-ledger.md) maps all use cases + and author tasks to evidence and remaining work. F0.2 is still open. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 229 passed, no skipped; local macOS/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Independent/sequential/reordered/regrouped records agree; M=1/8/32, K=1/2, + failure/retry, best reconstruction, ID/data changes and isolated imports checked. + +## Failed Attempts + +- Initial tests failed collection before the module existed; formatting lint fixed. +- Review found that an opaque digest alone cannot verify claimed prepared row order. + Add immutable row metadata and a counterexample where every role shares the wrong order. + +## Risks and Follow-ups + +- This is a sequential unit-weight squared-loss probe, not a production scheduler, + reusable cache or batched/GPU implementation. No timing claim follows from regrouping. +- Full artifacts, runtime callbacks and other recipe integration remain pending. +- Next D1/D3/D4 exact fixtures, then remaining grow/integration references and F0.3. + +## Commits + +- This slice: `test: add identity and isolated run references for v1`. diff --git a/learnings/2026-09-05-v1-mixed-vector-growth-reference.md b/learnings/2026-09-05-v1-mixed-vector-growth-reference.md new file mode 100644 index 0000000..0d3e760 --- /dev/null +++ b/learnings/2026-09-05-v1-mixed-vector-growth-reference.md @@ -0,0 +1,51 @@ +# 2026-09-05: Full mixed-feature and vector growth references + +## Context + +The F0.2 ledger still needed native categorical full trees, multilevel vector +payloads and training-transform-to-raw-prediction integration. + +## Decision or Result + +Bind fitted transforms to the reference tree and retain full vector leaves even +when split statistics are projected. A split that helps one output can have +negative total gain because splitting another output incurs extra regularization. + +## Changes + +- [Sprint 009](../v1-sprints/009-mixed-vector-growth-reference.md) records the + bounded plan, failed assumption, tests, results and reflection. +- Independent exhaustive mixed-feature grow for depthwise/best-first/symmetric, + immutable transform/tree records, full vector leaves and identity integration. +- 24 tests plus isolated production-import checks; original scalar/stump references + remain separate comparisons. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 279 passed, no skipped; local macOS/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Multilevel two-round leaves, K=1 scalar comparison, three growth policies, + categorical equality/unknown, row conservation, weight replication and raw transforms. + +## Failed Attempts + +- Initial collection failed on the absent module. +- Initial vector fixture assumed a second split helped the overall objective. + Summed gain was actually -1 under lambda=1; retain the correct no-split case, + and use a distinct positive-gain fixture to exercise full depth. + +## Risks and Follow-ups + +- Reference layout and immutable records are not production data/artifact APIs. + No leaf budget, GPU, real quality or serialization claim is made here. +- Next finite offset/two-stage and best/RNG integration, then audit F0.2 and freeze + F0.3. Full required scope remains unchanged. + +## Commits + +- This slice: `test: add mixed feature and full vector growth references for v1`. + +Pre-commit review also added explicit finite checks for combined candidate/layer +scores: finite child scores can overflow when summed. The added counterexample +passes; the final suite count is 279. diff --git a/learnings/2026-09-05-v1-normal-formula-reference.md b/learnings/2026-09-05-v1-normal-formula-reference.md new file mode 100644 index 0000000..bb1e80e --- /dev/null +++ b/learnings/2026-09-05-v1-normal-formula-reference.md @@ -0,0 +1,47 @@ +# 2026-09-05: Normal and Formula directional updates + +## Context + +F0.2 A11/A12 need geometry that differs from scalar Newton leaf fitting, including +joint/ordered parameters and rejection without residual state. + +## Decision or Result + +Solve unweighted Fisher/GGN directions before weighted least-squares tree fitting. +Evaluate candidate updates with the original loss. Full GGN does not establish +identifiability; a single structure input admits distinct equivalent parameters. + +## Changes + +- [Sprint 006](../v1-sprints/006-normal-formula-reference.md) records the bounded + plan, formulas, verification, reflection and remaining F0.2 obligations. +- Independent Normal/Fisher, Formula/Jacobian/GGN, explicit 2×2 direction solves, + initialization, separate Normal evaluator and immutable candidate-update probe. +- 25 tests plus production import isolation; no public trainer or runtime API. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 208 passed, no skipped; local macOS/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Hand Fisher solve, finite differences/Jacobian, CRPS/NLL, all three Formula + directions, two-round joint/ordered updates, weight replication, rejected trials + and exact reconstruction from accepted terms. + +## Failed Attempts + +- Initial tests failed collection before the reference module existed. +- Initial lint exposed test formatting; fixed before final verification. +- Review identified positive softplus parameters underflowing to zero; explicitly + reject that unsupported range, rather than silently changing formula support. + +## Risks and Follow-ups + +- No real quality, parameter recovery, persistence, CUDA or E-gate is established. + Update records do not implement the full best-state/callback/runtime contract. +- Next: state/run and identity references plus remaining acceptance mapping. + Complete other F0.2 gaps and F0.3 before F1 public implementation. + +## Commits + +- This slice: `test: add Normal and Formula directional update references for v1`. diff --git a/learnings/2026-09-05-v1-positive-aft-reference.md b/learnings/2026-09-05-v1-positive-aft-reference.md new file mode 100644 index 0000000..8742e42 --- /dev/null +++ b/learnings/2026-09-05-v1-positive-aft-reference.md @@ -0,0 +1,50 @@ +# 2026-09-05: Exposure roles and stable censored AFT references + +## Context + +F0.2 A7–A10 need independent positive-target and censored-likelihood formulas, +including exposure semantics and a consistent paid-count/severity target. + +## Decision or Result + +Treat Poisson exposure as an offset and annualized Tweedie exposure as a weight. +Keep event density and censored probability distinct. For far-right Normal tails, +retain the inverse-Mills correction directly so curvature does not cancel. + +## Changes + +- [Sprint 005](../v1-sprints/005-positive-aft-reference.md) records the bounded plan, + hand values, tail verification, remaining work and three-commit reflection. +- NumPy/stdlib Poisson/Gamma/Tweedie, explicit bases/outputs, policy join probe, + log-normal event/right-censored AFT and output transforms; no production API. +- 61 tests and extended production-import isolation. No new dependencies. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 183 passed, no skipped, local macOS/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Finite differences, two-round independent leaves, weight replication, exposure, + policy association and time-density Jacobian tests. Far tails agree with erfc + or independent Gauss-Laguerre quadrature at the declared fixture points. + +## Failed Attempts + +- Initial test collection failed on absent modules before implementation. +- An expanded unit-change test tried comparing a heterogeneous loss/g/h tuple as + one NumPy array. Compare its fields separately; this was a test-container error, + not evidence that the likelihood failed the unit transform. + +## Risks and Follow-ups + +- These are small mathematical references, not real ETL, insurance models or + clinical results. Complete two-stage training/persistence, IPCW and real-data + quality remain pending. Left/interval censoring and truncation are unsupported. +- Numeric domains must be representable; no silent clipping promises universal + extreme-range support. F1 production implementations still need conformance. +- Continue Normal/Formula, identity/state/run, remaining grow and F0.3 freeze; + all A1–A13 remain individually required. + +## Commits + +- This slice: `test: add positive target and AFT references for v1`. diff --git a/learnings/2026-09-05-v1-ranking-quantile-vector-reference.md b/learnings/2026-09-05-v1-ranking-quantile-vector-reference.md new file mode 100644 index 0000000..b52d417 --- /dev/null +++ b/learnings/2026-09-05-v1-ranking-quantile-vector-reference.md @@ -0,0 +1,49 @@ +# 2026-09-05: Distinct split and leaf mathematics for ranking, quantiles and vectors + +## Context + +F0.2 needs A4/A5/A6 counterexamples to test whether the foundation boundaries can +express query-local geometry, residual-based leaf solvers and vector payloads. + +## Decision or Result + +Keep pair aggregation, split statistics and leaf solving separate. A correct +quantile leaf does not imply the pseudo-gradient split objective will select a +split; this is a real quality limitation to evaluate, not a reason to force a tree. + +## Changes + +- [Sprint 004](../v1-sprints/004-ranking-quantile-vector-reference.md) records plans, + exact normalization/sketch semantics, verification and reflection. +- Independent all-pairs ranking, frozen lambda weights/NDCG, pinball/residual + quantile trees, shared vector stumps, split projection and train-only target scaling. +- 27 new tests plus extended production-import isolation. No production API added. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 122 passed, no skipped; macOS CPU/Python3.12.12/NumPy2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- Hand leaves, finite differences for pair/frozen-lambda geometry, query shifts, + quantile nonsmooth optimality, two-round updates and output permutation/replication. +- No CUDA, real-data quality, persistence, sampling or formal E-gate was exercised. + +## Failed Attempts + +- Initial missing-module collection failure preceded implementation. +- Initial quantile two-round fixture assumed a profitable pseudo split despite + weighted ties at its median. Keep this no-split counterexample; use another + weight distribution with positive split gain for the residual-update test. + The detailed derivation and fixtures are in Sprint 004. + +## Risks and Follow-ups + +- Vector coverage is a shared stump probe, not a full vector growth implementation. + Split projection uses an explicitly declared diagonal sketch; full leaf outputs + remain intact. Ranking sampling and query metric aggregation artifacts are pending. +- Complete positive/count and survival/AFT references, Normal/Formula, identity, + state/run and remaining growth checks before F0.3/F1. All use cases remain required. + +## Commits + +- This slice: `test: add ranking quantile and vector leaf references for v1`. diff --git a/learnings/2026-09-05-v1-sprint-execution.md b/learnings/2026-09-05-v1-sprint-execution.md new file mode 100644 index 0000000..86c1db5 --- /dev/null +++ b/learnings/2026-09-05-v1-sprint-execution.md @@ -0,0 +1,50 @@ +# 2026-09-05: Execute v1 through scoped sprints and explicit reflection + +## Context + +The user authorized execution and requested `v1-sprints/` for planning, records +and periodic reflection against the existing v1 plan. + +## Decision or Result + +Use [the sprint index](../v1-sprints/README.md) as the execution entry point. +The architecture and E-gates stay in planning; do not create competing scope. +Reflect at sprint closure, every three implementation commits, phase transitions +and architectural/correctness counterexamples. Keep concrete evidence and decisions. + +## Changes + +- [Sprint 001](../v1-sprints/001-scalar-tree-reference.md) scopes the first B01/F0.2 + scalar/tree reference slice and names independent failure cases before implementation. +- Agent guide and main plan point execution and reflection to the sprint directory. +- Sprint 001 now delivers standalone NumPy scalar/tree oracles with three growth + policies, exact routed cohorts and two-round traces; details and reflection + remain in the sprint record rather than duplicated as another plan. + +## Verification + +- Read the clean `9700845` starting state, plan, contracts, old split implementation, + public experimental docs and tests. No `tests/v1/` or `v1-sprints/` existed. +- Sprint bootstrap checks passed: six Markdown files, 43 resolving local links, + balanced fences and `git diff --check`. No production behavior changed. +- Reference implementation: 55 focused CPU tests passed, no skips; `uv run + --no-sync ruff check tests/v1` passed. Exact command/environment and the initial + missing-module failures are recorded in Sprint 001. A clean process blocks + all production imports while executing all three reference growth policies. + +## Failed Attempts + +- Reusing the normal root test setup would load production OpenBoost through + `tests/conftest.py`; reference tests need an isolated entry point and import check. + +## Risks and Follow-ups + +- Sprint 001 is only part of F0.2. Its results cannot mark the full reference + matrix, production foundation, quality, GPU performance or adoption complete. + +## Commits + +- `9700845` — preceding architecture design. +- Sprint bootstrap: `docs: start v1 sprint execution and reflection log`. +- `e76a2cd` — sprint bootstrap. +- Reference slice: `test: add independent scalar and tree references for v1`. diff --git a/learnings/2026-09-06-english-repository.md b/learnings/2026-09-06-english-repository.md new file mode 100644 index 0000000..e393d08 --- /dev/null +++ b/learnings/2026-09-06-english-repository.md @@ -0,0 +1,52 @@ +# 2026-09-06: English repository prose + +## Context + +The user requested that all repository files be in English while A2 evaluation +preparation was in progress. This applies to existing prose and future work. + +## Decision or Result + +Translate documentation, plans, sprint records, and historical strategy prose. +Preserve identifiers, mathematical notation, literal dataset values, raw evidence, +and historical decision boundaries. Require English in canonical AGENTS guidance. +The active foundation scope and evaluation-first execution order remain unchanged. + +## Changes + +- [Sprint 015](../v1-sprints/015-english-repository.md) records the plan, checks, + results, and reflection. +- Active planning/evaluation/application documents and historical GPU plans now + use English, along with the existing sprint records and strategy learning. +- Three stale links to retired implementation files now point to their locally + verified historical revision `05cd8bc800595a2f40c4d08f51afb697968b9b3e`. +- Adult data preparation was committed separately as `594519f`. + +## Verification + +- Repository text inventory: no remaining CJK ideographs; remaining non-ASCII + letters are mathematical notation. Ignored build caches and binary files are + outside the repository prose audit. +- Source URL/hash comparison, Markdown local-link/anchor/fence checks, and + `git diff --check`: pass. Link validation does not assert remote availability. +- Full current suite: 396 passed, no skips. Ruff and strict MkDocs build: pass. + Exact commands are recorded in Sprint 015. Environment: macOS, Python 3.12.12. +- Benchmark raw artifacts and executable code were not changed by translation. + +## Failed Attempts + +- An initial simple link regex misread a mathematical expression as a link. + Replaced that check with Markdown parsing, including fenced-code handling. +- The link audit found three existing targets removed during production retirement; + verified their contents exist in the historical Git tree and pinned the links. + +## Risks and Follow-ups + +- Text/structure checks complement semantic translation review; they are not a + proof that all prose is equivalent. Requirements and quantitative gates remain + authoritative and must not be relaxed during execution. +- Complete F0.3 evaluation preparation next; no new production or CUDA claim. + +## Commits + +- This slice: `docs: use English throughout repository prose` (parent `594519f`). diff --git a/learnings/2026-09-06-v1-a6-multioutput.md b/learnings/2026-09-06-v1-a6-multioutput.md new file mode 100644 index 0000000..e0a7529 --- /dev/null +++ b/learnings/2026-09-06-v1-a6-multioutput.md @@ -0,0 +1,56 @@ +# 2026-09-06: Multi-output regression closes a concrete CPU coverage gap + +## Context + +Sprint 035 found vector tree primitives but no complete A6 regression recipe. +Parent 83f6a9a; verification used this slice's dirty tree. + +## Decision or Result + +Compose independent scalar or shared vector trees with one joint transaction. +Expose split projections without reducing leaf/output dimension. Fit target +scaling on training weights only, preserve constant flags and persist inverse +scaling for original-unit predictions. Reject censored target semantics even +when all bounds happen to be finite. + +## Changes + +- objectives/recipes: MultiSquared, multi_squared and per-output MSE traces. +- multioutput.py: owned training-only scaler and original-unit inference artifact. +- Tests/docs: independent multi-round policies, projections, K=1, permutation, + scaling, rejection and mixed fresh-process inference. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_multioutput.py -n 0 -q: 14 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 730 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_multioutput.py: + passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-multioutput-wheel-001; + uv pip install --offline --python /tmp/openboost-multioutput-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp verified installed imports and executed every + docs/v1/*.md Python block in fresh namespaces: eighteen passed. + Build hash: [Sprint 036](../v1-sprints/036-a6-multioutput.md). + +## Failed Attempts + +Initial MultiSquared import failed before implementation. Constant-target test +then exposed floating weighted-mean error in variance-based constant detection. +Direct equality over positive-weight rows fixed the root cause. One unused +test import was removed by lint. + +## Risks and Follow-ups + +Raw recipe does not implicitly standardize targets. Callers preserve positional +output meaning and apply the saved scaler. Real subject-split A6 evaluation, +per-target comparisons, CUDA and performance remain unverified. Next follow +Sprint 035 item 2: prepared reuse and independent stopping/M32, not a GPU jump. + +## Commits + +Committed with this cohesive A6 slice; parent 83f6a9a. diff --git a/learnings/2026-09-06-v1-adult-data.md b/learnings/2026-09-06-v1-adult-data.md new file mode 100644 index 0000000..9734b73 --- /dev/null +++ b/learnings/2026-09-06-v1-adult-data.md @@ -0,0 +1,40 @@ +# 2026-09-06: Adult official test and typed raw records + +## Context + +Evaluation preparation remains the priority. A2 requires an unchanged official test +set, stratified splits within official training, missing categories and unit weights. + +## Decision or Result + +Pin the UCI archive, member bytes, typed parsed records and source-qualified row IDs. +Exclude fnlwgt, preserve categorical None, and use a fixed two-class label order. +The test set stays identical across all five seeds. Matching predictors across +sources are recorded without assuming they identify the same person. + +## Changes + +- [Sprint 014](../v1-sprints/014-adult-data-freeze.md) records scope and reflection. +- [Adapter](../benchmarks/v1/adult.py) and [freeze](../benchmarks/v1/datasets/adult.json) + retain 32,561 training and 16,281 test rows. No fitted encoding or model is included. + +## Verification + +- Real archive `python -m benchmarks.v1.adult /tmp/openboost-v1-adult.zip --verify benchmarks/v1/datasets/adult.json`: matched. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: 396 passed, no skips. +- Ruff over production, benchmarks/v1 and tests/v1: pass. +- Strict MkDocs build: pass. macOS/Python3.12.12/NumPy2.3.5. + +## Failed Attempts + +- Preimplementation collection failed on the missing adapter, as expected. + +## Risks and Follow-ups + +- Category encoding, baseline capability smoke, resource budgets and quality + evaluators remain pending. No quality or GPU result is claimed. +- English-only repository prose is now an explicit user requirement. + +## Commits + +- This slice: `data: freeze Adult official test and stratified splits` (parent `f74df58`). diff --git a/learnings/2026-09-06-v1-artifact-integrity.md b/learnings/2026-09-06-v1-artifact-integrity.md new file mode 100644 index 0000000..2d5619a --- /dev/null +++ b/learnings/2026-09-06-v1-artifact-integrity.md @@ -0,0 +1,48 @@ +# 2026-09-06: Artifact integrity before evaluation gates + +## Context + +F0.2 references are complete. F0.3 needs to reject incomplete or inconsistent +benchmark evidence before interpreting model quality or algorithm authoring cost. + +## Decision or Result + +Implement an offline integrity judge with a deliberately separate `integrity_pass` +field and empty `gate_results`. It checks the declared matrix, cache identities, +worker/backend status and artifact bytes; it does not trust a producer's pass claim +as an independently evaluated quality result. + +## Changes + +- [Sprint 011](../v1-sprints/011-artifact-integrity-judge.md): plan, tests and reflection. +- [Integrity schema and CLI](../benchmarks/v1/README.md): strict JSON, complete cell + records, file hashes, finite numeric predictions, visible optional failures. +- Full manifest/cell cache identity includes code, environment, data/split, + preprocessing, config, seed and protocol; dirty code explicitly unsupported + until a patch digest can identify uncommitted contents. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 336 passed, no skips; macOS, Python 3.12.12. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py`: pass. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict`: pass. +- 48 adversarial checks, including a missing second fold with every application + still represented, stale cache inputs, nonzero workers and malformed predictions. + +## Failed Attempts + +- Preimplementation collection failed because the judge module did not exist. +- Review strengthened the missing-fold fixture: A-ID coverage alone cannot detect + a missing repeat/fold when the application still has another successful record. + +## Risks and Follow-ups + +- Declared provenance is not authenticated. Target alignment, metric recomputation, + actual protocol design, package hashes and budgets remain outside this slice. +- Real dataset acquisition/hashes and baseline capability smoke are next; no frozen + real manifest, runner, held-out tasks or E-gate evaluation is claimed yet. + +## Commits + +- This slice: `test: add v1 artifact integrity judge` (parent `bc68ab9`). diff --git a/learnings/2026-09-06-v1-auxiliary-metrics.md b/learnings/2026-09-06-v1-auxiliary-metrics.md new file mode 100644 index 0000000..ffcadd8 --- /dev/null +++ b/learnings/2026-09-06-v1-auxiliary-metrics.md @@ -0,0 +1,54 @@ +# 2026-09-06: Auxiliary quality reports and censoring support + +## Context + +Primary metrics alone left required calibration, class, support and uncertainty +reports absent. Survival scoring must not treat a censoring time as a death or +extrapolate an unavailable censoring distribution. + +## Decision or Result + +Implement prediction-space diagnostics with explicit unsupported/undefined states. +IPCW Brier uses the frozen training G and supported grid. Only the G values used +in the formula are required; longer follow-up can contribute survival past an +earlier grid point without G extrapolation. Harrell C is separately named and +never described as IPCW. Paired intervals remain descriptive for five folds. + +## Changes + +- [Auxiliary metrics](../benchmarks/v1/auxiliary.py), documented primary definitions + and tie/support conventions in the [evaluation README](../benchmarks/v1/README.md). +- [Quality report](../benchmarks/v1/quality_report.py): hashed survival/structure + support, complete fold scores, missing-auxiliary ledger and paired summaries. +- Ten metric tests plus a complete five-fold hashed-survival report counterexample. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_auxiliary_metrics.py tests/v1/test_quality_artifacts.py -n 0 -q`: + 13 passed. Hand calculations cover weighted AUC ties, censor exclusions, + event/censor G ties, invalid grid support and undefined concordance. +- Rehashed but invalid censoring support still fails; absence is recorded and + cannot turn into E3 acceptance. Exact bootstrap leaves global RNG untouched. + +## Failed Attempts + +No scoring failure in final fixtures. An initial lint check caught a missing +explicit zip strictness argument; it was corrected before commit. + +## Risks and Follow-ups + +These are diagnostics, not an E3 completion signal. Complete matrix/input-provenance +binding remains necessary. Survival auxiliary weights are currently unit-only; +Harrell C is not censoring-adjusted concordance. Structure strata do not establish +formula identifiability or parameter stability. Agent and execution gates remain open. + +## Commits + +- This slice: `eval: report auxiliary quality and supported survival scores`. + +A further structural-artifact test verifies five-fold integration and rejects +rehashed but permuted structural row IDs. The focused metric/artifact total is +14 passing tests. Final full-suite and lint/documentation verification are recorded +with this slice below. + +Final verification: all 472 v1 tests passed; Ruff and strict MkDocs passed. diff --git a/learnings/2026-09-06-v1-b03-cpu-state.md b/learnings/2026-09-06-v1-b03-cpu-state.md new file mode 100644 index 0000000..3bec632 --- /dev/null +++ b/learnings/2026-09-06-v1-b03-cpu-state.md @@ -0,0 +1,65 @@ +# 2026-09-06: First public CPU ownership and transaction components + +## Context + +The user approved B03–B06 construction overlapping unfinished F0.3. The foundation +should exercise public boundaries rather than grow benchmark preparation indefinitely. + +## Decision or Result + +Own numeric data/problem arrays, use explicit keyed run randomness, and preserve +immutable accepted/best models. Bytes-backed arrays prevent value writeability +from being re-enabled. Offsets remain outside cached raw; algorithm scoring owns +exactly-once weight/offset use. Content, run and version identity bind proposals +to their actual parent, including divergent histories with equal version numbers. + +## Changes + +- Public data.py: NumericData/Problem, content/order identity and CPU role validation. +- Public runtime.py: RunContext, keyed RNG, accepted/proposed state, preview/resolve. +- Public artifacts.py: vector constant terms, coefficients, raw prediction and + strict versioned inference JSON, independent of training objectives. +- Public docs and the active plan record implemented boundaries and approved overlap. + +## Verification + +- Full v1 suite: 509 passed, including 21 new public component cases. Existing + references still run with production imports blocked. +- Hand cases verify weighted loss 5.25, offset once over two commits, distinct + train/validation shapes, rejection identity, stale/cross-run/divergent-parent + rejection, atomic vectors and immutable best history. +- Caller mutation cannot alter owned arrays; keyed RNG reproduces after rejection + without consuming global NumPy RNG. +- Fresh-process numeric/missing/vector constant inference roundtrip; corrupt + versions, duplicate/unknown fields, nonfinite and wrong-width artifacts fail. +- Ruff, strict MkDocs and the runnable documentation example passed. +- uv build produced sdist/wheel. An isolated uv venv with offline wheel installation + ran Python -I outside the repo; imports resolved inside the installed environment, + and the public example plus vector/offset inference roundtrip passed. +- Wheel environment: Python 3.12.12, NumPy 2.3.5. Wheel SHA256: + bc4882c9f76de3db80b81ce25e58e75ff1f6c25d2fd5b7be97386408ea775bbf. + This is scoped packaging validation, not complete E6 acceptance. + +## Failed Attempts + +Initial lint found import ordering and an assigned test lambda; corrected. +The sandboxed build could not resolve PyPI DNS for hatchling; approved network +access allowed the build, without publication. The wheel-location assertion first +compared resolved /private/tmp to literal /tmp; resolving both sides fixed it. +No production behavior changed for that path assertion. + +## Risks and Follow-ups + +ConstantModel is a B03 inference probe, not a tree grower, boosting recipe or +training-resume checkpoint. Numeric data is unbinned; specialized target roles, +categories and CUDA remain later work. Read-only values are ownership discipline, +not a hostile-code security boundary; callers must not alter array metadata. +Algorithm callbacks own mathematical correctness. Best score minimizes explicitly. + +B04 builds shared numeric/tree operations, B05 squared/Normal recipes, and B06 +Formula/heterogeneous state probes before stabilization. All F0.3 audit gaps remain +open; no quality, speed, agent or adoption result is implied. + +## Commits + +- This slice: feat: add initial public CPU problem state and artifacts. diff --git a/learnings/2026-09-06-v1-b04-depthwise-tree.md b/learnings/2026-09-06-v1-b04-depthwise-tree.md new file mode 100644 index 0000000..86f449a --- /dev/null +++ b/learnings/2026-09-06-v1-b04-depthwise-tree.md @@ -0,0 +1,58 @@ +# 2026-09-06: Composable depthwise numeric trees + +## Context + +B04 had public scalar operations but no assembled production learner. The next +probe was whether a grower could use these operations and ordinary callbacks, +with inference state independent of training rows and an exhaustive oracle. + +## Decision or Result + +Depthwise growth composes public operations, preserving original routed positions. +A leaf cap selects highest-gain splits within a layer before allocating stable +child IDs. Scoring executes once per legal candidate; caching avoids a second +callback invocation changing the layer decision. The inference artifact owns its +transformer and explicit topology rather than relying on heap-index children. + +## Changes + +- `src/openboost/tree.py`: depthwise growth with scoring/legality/leaf callbacks; + immutable numeric tree inference, graph validation and strict JSON persistence. +- `tests/v1/test_public_tree.py`: independent topology/prediction comparisons, + callback changes, corruption rejection and fresh-process persistence. +- Public docs and Sprint 020 describe the exact implemented boundary. + +## Verification + +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 553 passed, including 26 tree cases. macOS, Python 3.12.12, NumPy 2.3.5. +- Exact topology and numerical leaf/prediction comparisons against independent + exhaustive row growth at five depth/leaf budgets with weights and missing data. +- Replaced feature scoring, independent information feasibility and leaf solving + change results; doubled custom leaves double learner output. +- Cycles, shared/unreachable nodes, invalid index/routing/leaf/schema/cuts, + duplicate fields, nonfinite callbacks and empty-child admission are rejected. +- Ruff, strict MkDocs and offline sdist/wheel build passed. All three public docs + examples ran under Python -I from an isolated installed wheel outside the repo. + Wheel SHA256: 55308281657b883dd9cdaa80d8ac9b6f95c195c12c88706a1e6fa7f4673eb714. + +## Failed Attempts + +The shell has no bare `python`; the documentation update was rerun using the +project interpreter. Initial lint flagged ambiguous child variable names and a +loop callback closure; explicit child names and a bound cache resolved these. +No acceptance criteria or independent reference behavior changed. + +## Risks and Follow-ups + +Dense CPU operations have no measured speed advantage. The tree is a scalar +learner, without ensemble base/coefficient/offset or transaction integration. +No categories, specialized/vector leaves, CUDA or resume checkpoint is implemented. +Next: tree terms in immutable run state and complete squared/Normal recipes, +then B06 Formula and heterogeneous run probes before stabilizing interfaces. +F0.3 and all full v1 evaluations remain open; no required application was removed. + +## Commits + +- This slice: feat: assemble composable depthwise numeric trees. diff --git a/learnings/2026-09-06-v1-b04-numeric-ops.md b/learnings/2026-09-06-v1-b04-numeric-ops.md new file mode 100644 index 0000000..dbf9e59 --- /dev/null +++ b/learnings/2026-09-06-v1-b04-numeric-ops.md @@ -0,0 +1,60 @@ +# 2026-09-06: Public numeric preparation and split operations + +## Context + +B03 established immutable CPU problem/run state. B04 now needs shared operations +that external algorithm code can compose, rather than a closed trainer. + +## Decision or Result + +Fit unweighted quantile cuts once, retain explicit missing masks and transformer +identity, and aggregate named additive row fields from actual selected rows. +Newton weighting happens once in a declared adapter; independent cohort information +is separate. Histogram prefix/suffix candidate sums are checked against the +structurally different exhaustive original-row reference. + +## Changes + +- Public binning.py: immutable numeric cuts, feature-major int32 codes and missingness. +- Public stats.py: named row fields, weight roles, once-weighted Newton adapter. +- Public ops.py: histogram, candidates, feasibility/scoring callbacks, deterministic + choice, identity-checked partition and scalar Newton leaf. +- Public documentation executes a constrained split using only public components. + +## Verification + +- Full suite: 527 passed, including 18 new numeric-operation cases. +- Quantiles/codes match the independent order-statistic oracle for minimum cuts, + duplicates, constants, all-missing values, one bin and unseen numeric ranges. +- Feasible conditions, gains, chosen split, left/right rows and leaves match + exhaustive row enumeration on weighted/missing/subset/empty fixtures. +- A public cohort constraint changes the winner; independent mass stays unweighted. + Two-level manual composition conserves every original row and matches leaves. +- Double weighting, foreign rows/binning, invalid routes and custom NaN scores fail. +- Overflowing quantile interpolation fails explicitly instead of losing cuts. +- Ruff, strict MkDocs and offline sdist/wheel build passed. Both B03 and B04 public + examples ran using an isolated installed wheel under Python -I outside the repo. + Wheel SHA256: 707cfcaee9187764bbe44da08c86745bc5566307cc79ef4fd8da80be0f23b48d. + +## Failed Attempts + +The initial cohort-winner fixture used four quantile bins for six rows, merging +the distinguishing first two rows. Six bins restored the intended independent +counterexample; no algorithm or acceptance threshold changed. A lint check also +required combining nested test contexts. + +## Risks and Follow-ups + +This is the operations slice, not a complete B04 tree grower. No tree artifact, +boosting recipe, categorical splitter or CUDA implementation exists yet. RowFields +metadata prevents API-level reweighting, not arbitrary plugin mathematical errors. +Histograms use dense CPU NumPy storage; no performance or memory-efficiency claim. +Extreme quantile interpolation may require explicit feature rescaling. + +Next: public depthwise assembly and validated tree prediction/persistence using +these same operations, then B05 squared/Normal recipes. B06 must probe Formula +and heterogeneous state before stabilization; F0.3 remains open. + +## Commits + +- This slice: feat: add composable CPU numeric split operations. diff --git a/learnings/2026-09-06-v1-b05-normal-recipe.md b/learnings/2026-09-06-v1-b05-normal-recipe.md new file mode 100644 index 0000000..0047e32 --- /dev/null +++ b/learnings/2026-09-06-v1-b05-normal-recipe.md @@ -0,0 +1,73 @@ +# 2026-09-06: Normal geometry shares scalar tree and transaction foundations + +## Context + +Squared boosting worked, but Problem/state coupled observed target width to raw +parameter width. Normal's scalar observation and two raw parameters exposed the +first necessary change to that initial state boundary. + +## Decision or Result + +Problem now declares raw_width independently, defaulting to observed target width. +Offsets align with raw parameters; state validates against raw_width. Normal uses +ordinary gradients or diagonal Fisher directions, then fits unweighted directions +through once-weighted least-squares statistics. Both scalar learners commit or +reject jointly through the same mapped terms and runtime as squared boosting. +Shared numerical trial handling retries smaller coefficients without swallowing +structural errors. No separate distributional trainer or duplicated targets were +introduced. This validates one additional geometry, not the full v1 abstraction. + +## Changes + +- data/runtime: independent raw width, strict offset shapes and model validation. +- objectives: Normal weighted NLL, unweighted gradient/Fisher diagonal, offset-aware + base and mean/scale output, plus ordinary/diagonal-natural direction operation. +- stats: public least_squares adapter with G=-w*z and H=w. +- recipes: joint Normal updates and shared configuration/trial helpers. Numerical + backtracking failures are recorded; fixed-step numerical failures still raise. +- Public Normal example and updated construction/capability documentation. + +## Verification + +- Initial raw-width test failed before implementation with unsupported keyword. +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 572 passed, including 12 new Normal/raw-width cases. Python 3.12.12, + NumPy 2.3.5, macOS. No CUDA validation. +- Ordinary and natural modes agree with independent three-round exhaustive + reference gradients, Fisher diagonals, directions, coefficients, losses and raw + predictions. Final NLL agrees with the separate density-based reference scorer. +- Offset-aware base satisfies weighted first-order conditions; geometry agrees + with explicit shifted-raw reference. Regression curvature equals original + weight, not Fisher curvature. Offsets remain outside accepted caches. +- Joint rejection preserves identical raw/model/best/version; two learners fit + once per round. Nonfinite trials continue to later finite evaluations without + committing invalid state. Wrong learner schemas raise before trial handling. +- Fresh-process persisted raw ensemble reproduces mean/scale outputs on unseen + numeric and missing observations. Invalid shapes/modes/damping/floors fail. +- Ruff, strict MkDocs and offline build pass. Five public examples pass under + Python -I from an isolated installed wheel outside the checkout. + Wheel SHA256: b6181e78dbde0856109b74b9d5a6b1b92194810fe31f84266bf15882d5a6549d. + +## Failed Attempts + +An initial file lookup assumed a distribution-prefixed reference filename; the +reference is coupled.py. Import ordering required routine lint fixes. Review of +numerical trial handling prompted validating schemas before the retry catch, so +structural errors cannot appear as ordinary full rejection. No oracle changed. + +## Risks and Follow-ups + +Normal currently uses joint updates and diagonal Fisher geometry. Ordered updates, +Formula full/GGN metric, heterogeneous runs and every other required use case +remain necessary. B06 is the next construction probe before stabilization. +Raw model artifacts do not store a distribution tag, interval calibration or +resume state; callers explicitly apply Normal output conversion. Scale flooring +is initial-only; later nonrepresentable distributions are rejected, not clipped. +Predictions are recomputed during trials and trace arrays are retained; there is +no large-workload performance, quality parity, GPU or adoption claim. F0.3 and +full evaluation gates remain open. + +## Commits + +- This slice: feat: add Normal boosting with independent raw parameter width. diff --git a/learnings/2026-09-06-v1-b05-squared-recipe.md b/learnings/2026-09-06-v1-b05-squared-recipe.md new file mode 100644 index 0000000..0efc3b9 --- /dev/null +++ b/learnings/2026-09-06-v1-b05-squared-recipe.md @@ -0,0 +1,69 @@ +# 2026-09-06: First complete squared CPU recipe + +## Context + +The depthwise scalar learner existed, but accepted state only represented constant +updates. B05 needs complete recipes sharing the same foundation and transactions. + +## Decision or Result + +Model replaces ConstantModel and the constant-only format, as permitted by the +clean-redesign instruction. Immutable tree terms carry a scalar learner, explicit +[1, K] output map and coefficient. Multiple terms can be proposed atomically. +The squared recipe composes public geometry, statistics, growth and transactions. +Fixed steps and bounded backtracking share this path; validation selects best +state independently of training acceptance. + +## Changes + +- `artifacts.py`: mapped tree/constant ensemble, strict nested tree persistence, + conservative finite-output envelope and separate inference offsets. +- `runtime.py`: atomic term tuples and owned direct proposal construction. +- `objectives.py`: scalar squared base, unweighted gradients, once-weighted fields + and weighted mean half-square loss with offsets applied outside raw caches. +- `recipes.py`: complete fixed/backtracking loop and per-round evidence, with an + ordinary custom learner callable. Conflicting growth options fail explicitly. +- Existing state tests retain their mathematical checks using the new model name. + +## Verification + +- Initial focused recipe test failed with ModuleNotFoundError before implementation. +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 560 passed, including seven new squared/ensemble tests. Python 3.12.12, + NumPy 2.3.5, macOS; no CUDA execution. +- Three rounds match independent exhaustive reference gradients, losses, raw + predictions and base with nonuniform weights, zero weight and missing features. +- Offset-shift equivalence, distinct validation caches and earlier best retention. +- Backtracking fits one learner, tries 16/8/4/2 and accepts 2; a six-trial rejection + preserves terms, version, best model and identical raw state across rounds. +- Mixed mapped-tree/constant terms commit jointly; fresh-process inference matches + after persistence on unseen numeric and missing inputs with explicit offsets. +- Corrupt nested topology/maps/coefficients/kinds and invalid zero-round options + are rejected. Direct Proposal copies its term sequence before exposing it. +- Ruff, strict MkDocs and offline build pass. All four public docs examples pass + under Python -I from an isolated installed wheel outside the checkout. + Wheel SHA256: f16630722fc448a301e036972cd0723329da85194f3edea7ea91a2183272d560. + +## Failed Attempts + +Initial lint required sorting imports and replacing a test lambda assignment. +Final review found direct Proposal construction could retain a mutable term list; +normalizing to an owned tuple closed that path and a regression test covers it. +No oracle or acceptance threshold was changed. + +## Risks and Follow-ups + +This is the squared half of B05, not completion of Normal, F1 or v1 evaluation. +Normal requires raw parameter widths distinct from observed target widths; the +current Problem/state shape contract still couples them. That is the next design +probe, followed by Formula and heterogeneous runs in B06 before stabilization. +Backtracking reuses fitted trees but recomputes ensemble predictions. Traces retain +round arrays; no large-workload memory or performance claim is made. The finite +absolute-value envelope may reject extreme terms that would cancel. Artifacts +support inference only, not training resumption. Categories, vector/structured +leaves, CUDA and full A1–A13 evaluation remain required future work. + +## Commits + +- This slice: feat: compose squared boosting with mapped tree transactions. diff --git a/learnings/2026-09-06-v1-b06-formula-runs.md b/learnings/2026-09-06-v1-b06-formula-runs.md new file mode 100644 index 0000000..9448992 --- /dev/null +++ b/learnings/2026-09-06-v1-b06-formula-runs.md @@ -0,0 +1,75 @@ +# 2026-09-06: Formula and heterogeneous runs reuse the foundation + +## Context + +B06 tests whether coupled structured geometry and multiple independent algorithms +can reuse the scalar operations and state established by squared/Normal recipes. +This is also the reflection point after three recipe implementation commits. + +## Decision or Result + +Owned named structure binds row-aligned auxiliary inputs without becoming split +features. Saturation Formula exposes a full rank-one GGN; a damped SPD solve +produces unweighted directions, fitted through the same least-squares adapter, +trees, mapped terms and transactions as Normal. No diagonal fallback is hidden. +Sequential RunSpec execution shares immutable feature inputs while keeping +contexts, target/raw widths, round budgets, best states and error records separate. + +## Changes + +- data: owned structural role map and identity; unused structure is explicitly + rejected by squared/Normal instead of silently ignored. +- objectives/recipes: saturation Formula prediction/base/geometry, damped full + direction solve, joint updates and intermediate evidence. +- runs: immutable scalar options, unique-ID validation, sequential outcome/error + records, returned-state identity checks and continuation after ordinary errors. +- Public Formula/run-many usage and capability boundaries documented. + +## Verification + +- Initial structural-role test failed before implementation with unsupported keyword. +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 581 passed, including nine new Formula/run tests. Python 3.12.12, + NumPy 2.3.5, macOS. No CUDA execution. +- Three Formula rounds agree with independent explicit-inverse/exhaustive-growth + reference gradients, full GGN, directions, coefficients, losses and raw output. +- Zero-damping rank deficiency fails; offset geometry agrees with explicitly + shifted reference raw. Structure changes problem identity without changing + feature identity; source mutation and writable re-enabling cannot change roles. +- Fresh-process persisted raw ensemble reproduces Formula output with separately + supplied inference structure on numeric/missing/unseen observations. +- M=1/2/8 mixed recipes with K=1/2 and 0/1/2 rounds match independent same-ID and + reversed-order results, with disjoint raw caches. Injected failure is recorded + and the subsequent valid run completes. Duplicate IDs fail before any work. +- Invalid roles, unused structure, option overrides, fused mode and foreign + returned state are rejected; options are copied from caller mappings. +- Ruff, strict MkDocs and offline build pass. All six public examples pass under + Python -I from an isolated installed wheel outside the checkout. + Wheel SHA256: 4f04e5e793035b0e232566a42bca9580256b3aab25afa88896a02892033ac310. + +## Failed Attempts + +Initial lint required import sorting. No numerical fixture or independent oracle +needed adjustment. Singular Formula geometry was an expected counterexample, +handled by explicit damping rather than weakened acceptance or pseudoinversion. + +## Risks and Follow-ups + +This is one saturation formula and sequential execution, not arbitrary symbolic +programs, early-stopping callbacks, resource scheduling, process isolation or +fused train-many. Each recipe still fits its own binning; only NumericData is +shared in the initial scheduler. Raw artifacts require explicit output transform +and structural inputs at inference. Full metrics here are tiny dense two-parameter +matrices, not a high-dimensional memory guarantee. Arbitrary recipe callbacks +must respect input ownership; execution is not a sandbox. + +The three recipes support the geometry/statistics/state boundary but do not prove +that agents make changes faster or that all required applications work. Continue +B07 growth policies/categories, then vector and specialized leaf probes without +freezing these interfaces. Ordered Normal and all remaining application/evaluation +requirements remain open. F0.3 is incomplete. No quality/speed/GPU/adoption claim. + +## Commits + +- This slice: feat: add Formula geometry and sequential heterogeneous runs. diff --git a/learnings/2026-09-06-v1-b07-categorical.md b/learnings/2026-09-06-v1-b07-categorical.md new file mode 100644 index 0000000..4c7e41f --- /dev/null +++ b/learnings/2026-09-06-v1-b07-categorical.md @@ -0,0 +1,73 @@ +# 2026-09-06: Categorical equality shares numeric growth operations + +## Context + +All three growth policies existed but interpreted conditions as numeric thresholds. +B07 requires typed category preparation, equality splits and persistent missing/ +unseen routing through the same foundation rather than an alternate trainer. + +## Decision or Result + +MixedData owns tuple-backed mixed feature rows and explicit feature kinds. Binning +fits numeric cuts or sorted homogeneous string/integer dictionaries on training +inputs only. Categorical candidates select one value versus the rest, with both +missing routes. Unknown values follow missing routes. Histogram counts/fields, +choice, growth and transactions remain shared. + +Binning/Tree replace NumericBinning/NumericTree with no compatibility aliases. +The tree format is openboost-tree-v2, including typed dictionaries; old numeric +formats fail loading. NumericData remains a numeric-only input. MixedData.values +is a detached array export, not a writable view of owned state. + +## Changes + +- data/binning: mixed input, dictionary validation, explicit schema kinds and + feature-major codes/missing masks with transformer identities. +- ops/tree: equality candidate statistics and routing, persistent dictionary + conditions, schema and category-index validation across all three policies. +- artifacts/recipes: mixed input through existing scalar recipes and mapped terms. +- Tests/docs migrate public names while preserving independent numeric references + and prior mathematical/state checks. Historical evidence records remain intact. + +## Verification + +- Initial mixed-input test failed before implementation with missing MixedData. +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 615 passed, including 12 categorical cases. Python 3.12.12, NumPy 2.3.5, macOS. +- All three mixed-feature policy topologies/leaf values/predictions agree with + independent exhaustive raw-row reference under nonuniform/zero weights. +- String/integer/all-missing dictionaries agree with independent CategoryMap; + both equality missing routes conserve rows and weighted candidate sums. +- Fresh-process squared ensemble persistence preserves numeric/category/unseen/ + missing predictions. All-missing and missing-only categorical cases work. +- Invalid dictionaries, empty referenced dictionaries, foreign feature kinds, + transformer identities and invalid/mixed category token types are rejected. +- Ruff, strict MkDocs and offline build pass. Every code example across seven + public documentation pages passes under Python -I from an isolated installed + wheel outside the checkout. + Wheel SHA256: 4d632759ee2dd9293419e3ede66d6c4d7454d9228983c15f86d504084463ef94. + +## Failed Attempts + +An unused corruption-case label needed a lint rename. Review found the all-missing +histogram's placeholder bin must not be accepted as a real category condition; +Tree validates against dictionary length rather than padded histogram capacity. +No independent reference or mathematical acceptance threshold changed. + +## Risks and Follow-ups + +This is one-category-versus-rest splitting, not subset search or ordered target +statistics. MixedData exports object-array copies; no throughput or memory claim. +Inference artifacts are raw models, not resume checkpoints or calibrated output +schemas. Complete squared composition is verified here; accepting mixed inputs in +Normal/Formula does not establish their real-data categorical quality. + +Next: B08 class schema, classification and vector-leaf/mapping probes. Ordered +updates, specialized leaf/objective families, CUDA, agent/adoption studies and +all required real application evaluations remain necessary. F0.3 and complete +F1/v1 acceptance remain open. No push or upstream-library parity claim. + +## Commits + +- This slice: feat: add categorical equality splits and mixed-feature artifacts. diff --git a/learnings/2026-09-06-v1-b07-growth-policies.md b/learnings/2026-09-06-v1-b07-growth-policies.md new file mode 100644 index 0000000..3210c75 --- /dev/null +++ b/learnings/2026-09-06-v1-b07-growth-policies.md @@ -0,0 +1,64 @@ +# 2026-09-06: Three public numeric growth policies + +## Context + +B07 requires distinct depthwise, best-first and symmetric growth semantics through +shared operations. The independent reference rescans leaves, allowing a structurally +different heap implementation to be checked without using production as its oracle. + +## Decision or Result + +Ordinary public policy functions share only local construction scratch and the +existing histogram/candidate/scoring/legality/routing/leaf operations. Best-first +caches unaffected leaf candidates in a heap. Symmetric growth intersects legal +conditions across the layer and sums all associated gains, including negative +individual gains. It never substitutes each node's independently best split. + +## Changes + +- tree: shared construction scratch, retained depthwise policy, new best_first + and symmetric functions using the same validated NumericTree artifact. +- Tests: exact independent topology/leaf/prediction comparisons, symmetric negative + local-gain counterexample, callback checks and complete recipe substitution. +- Public docs explain pure callbacks, common-layer semantics and full-layer budgets. + +## Verification + +- Initial comparison test failed before implementation with missing public growers. +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 603 passed, including 22 new growth cases. Python 3.12.12, NumPy 2.3.5, macOS. +- Three policies times five depth/leaf budgets match independent exhaustive + topology, leaf values and predictions with weighted rows and missing features. +- A symmetric layer accepts gains -1/+3 jointly and refuses a partial-layer + budget. Replacement feasibility restricts features; doubled custom leaf values + double output. Candidate scoring occurs once per node/condition. +- Best-first and symmetric three-round squared recipes match independent reference + predictions. Their ensemble round trips preserve unseen/missing predictions. +- Invalid depth/capacity/nonfinite callback values fail. All previous tree, + squared, Normal, Formula and run-state regression cases still pass. +- Ruff, strict MkDocs and offline build pass. All code examples across six public + documentation pages pass under Python -I from an isolated installed wheel. + Wheel SHA256: 50d76d90710f9945aded1e47f9aa7113e6a4735af33f2f8a9ea96b4497030645. + +## Failed Attempts + +No mathematical fixture or reference adjustment was required. The initial missing +imports were the intended pre-implementation failure. Formatting was normalized +before regression verification. + +## Risks and Follow-ups + +Callbacks must be pure with respect to candidate statistics and immutable config; +heap caching intentionally does not support call-count-dependent scoring. Shared +scratch is local per grow invocation, not shared run state. No performance claim +follows from using a heap. All policies remain numeric/scalar CPU implementations. + +Next B07 slice: categorical preparation, candidate conditions, routing and artifact +validation through these same operations. Vector/specialized leaves, ordered +Normal, stopping policies, CUDA and complete evaluation remain required. F0.3 +and full F1/v1 acceptance remain incomplete; nothing was pushed. + +## Commits + +- This slice: feat: add best-first and symmetric numeric growth policies. diff --git a/learnings/2026-09-06-v1-b08-binary.md b/learnings/2026-09-06-v1-b08-binary.md new file mode 100644 index 0000000..ddadc41 --- /dev/null +++ b/learnings/2026-09-06-v1-b08-binary.md @@ -0,0 +1,73 @@ +# 2026-09-06: Class schemas and stable binary boosting + +## Context + +B08 needs explicit label order and classification geometry, not regression over +unlabelled integer codes. Class schemas must survive every transaction and model +round trip so probabilities cannot silently change column meaning. + +## Decision or Result + +ClassSchema fits sorted homogeneous string/integer training labels, encodes known +labels and rejects unknown/missing labels. Problem identity includes schema; +accepted/best models require matching train/validation class order. Binary logistic +geometry uses signed-margin loss and separate sigmoid tails to preserve small +correct-class gradients. The recipe uses the existing weighted Newton fields, +mixed-feature trees, mapped terms and fixed/backtracking transactions. + +## Changes + +- data/runtime: class encoding/decoding, code validation, class-bound identities + and schema preservation/checks across initialization, proposals and best state. +- objectives/recipes: binary base, gradient/curvature/loss and complete recipe. + Regression objectives explicitly reject classification-tagged problems. +- outputs/artifacts: inference-only probability transform, decoded labels and + persisted class order in openboost-ensemble-v2. Earlier formats fail loading. +- Public binary example and explicit calibration/multiclass boundaries. + +## Verification + +- Initial class-schema test failed before implementation with missing ClassSchema. +- `OPENBOOST_BACKEND=cpu UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run + --no-sync pytest tests/ -m 'not gpu and not benchmark' --tb=short -q`: + 621 passed, including six binary/class-schema tests. Python 3.12.12, + NumPy 2.3.5, macOS. No CUDA execution. +- Three rounds agree with independent binary geometry and exhaustive tree updates + under nonuniform/zero weights and missing numeric inputs. +- Extreme logits preserve signed-margin loss and tiny gradient/curvature tails; + shifted-raw oracle agrees with offset geometry, and inference offsets apply once. +- Class maps agree with independent reference; unknown/missing/mixed labels, + out-of-range codes and mismatched validation schemas fail. +- Fresh-process mixed-feature classifier round trip preserves probability columns + and decoded integer labels, including unseen categories/missing numeric values. +- Corrupt class ordering/duplicates/token types/unsupported output widths fail. + Full rejection preserves state; earlier best models retain schema; ties decode + to the first sorted class. Both training classes and representable clip required. +- Ruff, strict MkDocs and offline build pass. All code examples across eight public + pages pass under Python -I from an isolated installed wheel outside the checkout. + Wheel SHA256: f3f3d1d42c4b212c26371b486bd870d2793d0cf7224a73553d776048ceace72b. + +## Failed Attempts + +Initial import ordering required lint normalization. No independent reference or +acceptance criterion was changed. Signed-margin/tail formulas were used directly +rather than relying on subtracting a saturated probability from one. + +## Risks and Follow-ups + +The current classifier artifact and recipe accept two classes and one raw column. +Schemas can describe more labels, but multiclass geometry/vector leaves remain the +next B08 slice. Offset initialization centers the clipped prevalence logit by the +weighted mean offset; it is not an optimized heterogeneous-offset intercept. +Probabilities are not a calibration or predictive-quality guarantee. Training +requires both observed codes, even when one class has zero total weight (handled +by the declared probability clip). + +Raw ensemble format changed deliberately under clean-redesign permission. No +resume checkpoints, early stopping callbacks or CUDA were added. Ordered updates, +all remaining specialized objectives and full real application/agent/adoption +measurements remain required; F0.3 and full v1 acceptance are open. + +## Commits + +- This slice: feat: add class schemas and binary logistic boosting. diff --git a/learnings/2026-09-06-v1-b08-vector-multiclass.md b/learnings/2026-09-06-v1-b08-vector-multiclass.md new file mode 100644 index 0000000..9e09b5a --- /dev/null +++ b/learnings/2026-09-06-v1-b08-vector-multiclass.md @@ -0,0 +1,61 @@ +# 2026-09-06: Vector leaves separate split and output dimensions + +## Context + +B08 needs multiclass and vector learners without duplicating tree growth or +restricting output dimension to split-statistic dimension. Sprint 027 starts at +6e5d6b0; verification ran with this slice's uncommitted changes. + +## Decision or Result + +All three existing growers accept separate split/leaf RowFields from the same +problem. Vector Newton fields retain original weights once. Shared topology +predicts [N,L], and explicit [L,K] mappings integrate with existing state. The +multiclass recipe uses one vector tree per round with softmax diagonal upper +bounds, preserving atomic rejection and persisted class order. + +## Changes + +- Stats/ops/tree: vector fields, per-channel diagonal leaves, summed gain and + full leaf statistics independent of projected split fields. +- Artifacts: validated matrix payloads in tree-v3, arbitrary output maps and + conservative per-channel finite envelope; earlier tree versions rejected. +- Objective/recipe/outputs: multiclass geometry, uniform-logit initialization, + joint vector updates and softmax probability/label inference. +- Public docs: runnable multiclass example and updated capability boundaries. + +## Verification + +Commands use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_vector_multiclass.py -n 0 -q: + nine passing cases, independent topology/leaf/prediction and softmax oracles. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" --tb=short: + 630 passed, Python 3.12.12 / NumPy 2.3.5, macOS. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_vector_multiclass.py: + passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-vector-wheel-001; uv pip + install --offline --python /tmp/openboost-vector-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl: passed. +- Isolated Python (-I, cwd /tmp) verified imported package was in that environment, + extracted every fenced Python block from docs/v1/*.md and executed each in a + fresh namespace: ten passed. +- Build identity and reflection: [Sprint 027](../v1-sprints/027-b08-vector-multiclass.md). + +## Failed Attempts + +The initial vector_newton import failed before implementation, confirming the +missing public boundary. No algorithm-oracle mismatch remained in final checks. + +## Risks and Follow-ups + +No CUDA, real-data quality, performance or agent/adoption claim. The diagonal +bound is not the exact softmax Hessian. All declared classes must appear in +training. Full A6 workflows and B09 ranking/quantile/penalized leaf work remain. +F0.3 is not closed by this construction slice. + +## Commits + +This entry is committed with the cohesive B08 vector/multiclass implementation. +Parent: 6e5d6b0. diff --git a/learnings/2026-09-06-v1-b09-quantile-leaves.md b/learnings/2026-09-06-v1-b09-quantile-leaves.md new file mode 100644 index 0000000..2535be9 --- /dev/null +++ b/learnings/2026-09-06-v1-b09-quantile-leaves.md @@ -0,0 +1,56 @@ +# 2026-09-06: Routed residual leaves preserve original weight mass + +## Context + +B09/A5/D3 require original residuals and weights at leaves; summed Newton fields +cannot provide them. Parent 55f3b91; verification used this slice's dirty tree. + +## Decision or Result + +Separate owned residual context from weighted additive fields, retain global +row IDs in routed views, and validate matching problem identity. Ordinary and +penalized quantile solvers share all three growth policies. Positive penalties +are solved with a monotone subgradient scan, independently checked against +breakpoint/interior enumeration in the D3 reference. + +## Changes + +- leaves.py: owned residual context/views and exact scalar weighted pinball solver. +- tree.py: paired routed callback/context, rejecting ambiguous additive solvers + and foreign problems. +- objectives/recipes: quantile base, pseudo-statistics and fixed/backtracking + rounds; split regularizer and anchored leaf penalty remain separate. +- Documentation/tests: public runnable example and explicit CPU-only boundaries. + +## Verification + +Commands use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_quantile.py -n 0 -q: 13 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 655 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_quantile.py: passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-quantile-wheel-001; + uv pip install --offline --python /tmp/openboost-quantile-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp checked the installed module path and executed + every docs/v1/*.md Python block in a fresh namespace: twelve passed. + Build hash and detailed reflection: [Sprint 029](../v1-sprints/029-b09-quantile-leaves.md). + +## Failed Attempts + +Initial public ResidualContext import failed before implementation. Ruff found +two unused imports during test development; removed before final checks. + +## Risks and Follow-ups + +ResidualContext is specifically scalar; it does not establish the final linear +or arbitrary structured-leaf context. Tests show CPU correctness, not real A5 +quality, calibrated/noncrossing quantiles, GPU speed or lower author effort. +B10 positive-target/AFT construction is next; B09 pair-weight/sampling limitations +and broader workflow/evaluation gates remain open. + +## Commits + +Committed with this cohesive B09 routed quantile slice; parent 55f3b91. diff --git a/learnings/2026-09-06-v1-b09-ranking.md b/learnings/2026-09-06-v1-b09-ranking.md new file mode 100644 index 0000000..aad51dc --- /dev/null +++ b/learnings/2026-09-06-v1-b09-ranking.md @@ -0,0 +1,55 @@ +# 2026-09-06: Query dependencies reduce to shared scalar fields + +## Context + +B09 must handle ranking without treating pair/query dependencies as independent +row weights. Parent revision: 08e323a. Verification used this slice's dirty tree. + +## Decision or Result + +Ranking validates query roles and constant-within-query weights, reduces all +eligible query-local pairs into scalar Newton fields, and composes the existing +grower and state operations. Lambda ranks are recomputed each round. NDCG uses +stable row-ID score ties and query-weighted averaging; zero-ideal queries score one. + +## Changes + +- ranking.py: public pairwise/lambda geometry and NDCG score with explicit query + roles; reject non-unit row weights and unsupported structure. +- recipes.py: fixed-step ranking, per-round geometry evidence, validation NDCG + best-model selection. No new tree format or persistence path. +- Documentation and tests: runnable example, scoped limitations and independent + multi-round/finite-difference/query-isolation/persistence verification. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_ranking.py -n 0 -q: 12 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 642 passed on macOS, Python 3.12.12, NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_ranking.py: passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-ranking-wheel-001; + uv pip install --offline --python /tmp/openboost-ranking-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp asserted the installed package path and executed + every docs/v1/*.md fenced Python block in a fresh namespace: eleven passed. + Build SHA256 is recorded in [Sprint 028](../v1-sprints/028-b09-ranking.md). + +## Failed Attempts + +The first test failed to import openboost.ranking before implementation. +Ruff identified an unused import in the initial test; it was removed. + +## Risks and Follow-ups + +All-pairs memory/time is quadratic within each query. Explicit pair weights, +sampling and ranking line search are deferred rather than silently ignored. +The recipe does not verify real dataset partition independence. Real A4 results, +CUDA, and quality/performance parity remain open. Next build routed-row access +for quantile/penalized leaves; additive sums alone do not satisfy that contract. + +## Commits + +Committed with the cohesive B09 ranking slice; parent 08e323a. diff --git a/learnings/2026-09-06-v1-b10-aft.md b/learnings/2026-09-06-v1-b10-aft.md new file mode 100644 index 0000000..ac0ac2e --- /dev/null +++ b/learnings/2026-09-06-v1-b10-aft.md @@ -0,0 +1,55 @@ +# 2026-09-06: Censoring is an explicit target contract + +## Context + +B10/A10 requires event/right-censored log-normal AFT with valid upper infinity +and persisted scale. Parent b9d5ace; checks used the slice's dirty tree. + +## Decision or Result + +Problem.target_kind="event_right" validates [lower,upper] bounds and defaults to +one raw location output. Ordinary numeric targets remain finite. AFT geometry +reduces to the existing scalar Newton path. Normal-tail quadrature avoids +large-z cancellation and is checked against an independent continued fraction. +AFTModel persists fixed scale with the raw model and output transformations. + +## Changes + +- data.py: target-kind validation, identity and raw-width default. +- survival.py: fixed-scale geometry, stable tails and scale-aware inference artifact. +- recipes.py: fixed/backtracking AFT rounds and per-round evidence. +- Tests/docs: censored likelihood, target bounds, multiple scales and loaded outputs. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_aft.py -n 0 -q: 23 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 716 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_aft.py: passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-aft-wheel-001; + uv pip install --offline --python /tmp/openboost-aft-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp asserted the installed package path and executed + every docs/v1/*.md Python block in fresh namespaces: seventeen passed. + Build hash/reflection: [Sprint 034](../v1-sprints/034-b10-aft.md). + +## Failed Attempts + +The initial LogNormalAFT import failed before implementation. Initial test lint +reported semicolon statements and an unused import; formatting/import cleanup +resolved them before final checks. + +## Risks and Follow-ups + +Initializer is not a censoring-adjusted MLE. Only fixed-scale exact events and +right censoring are supported; no real A10, IPCW/calibration, learned scale, +other censoring forms, CUDA or performance claim. AFTModel declares scale; +callers must use the training scale. Audit B11 CPU prerequisites and remaining +A6/extension/application gates next; no phase exit follows from test count. + +## Commits + +Committed with the cohesive B10 AFT slice; parent b9d5ace. diff --git a/learnings/2026-09-06-v1-b10-frequency-severity.md b/learnings/2026-09-06-v1-b10-frequency-severity.md new file mode 100644 index 0000000..ce97bab --- /dev/null +++ b/learnings/2026-09-06-v1-b10-frequency-severity.md @@ -0,0 +1,56 @@ +# 2026-09-06: Persist the dependency roles of a two-model mean + +## Context + +A9 requires matched positive-payment frequency/severity composition with saved +dependencies, distinct from Tweedie fitting. Parent bf7ef2c; checks used the +slice's dirty tree. + +## Decision or Result + +Bind declared paid-count/total aggregates to frequency and count-weighted +severity-average problems. Embed both raw models in a role-specific artifact. +Require aligned inference policy IDs and explicit exposure/offsets, and expose +rate, count, severity, annualized and period means. + +## Changes + +- composition.py: aggregate validation/problem assembly and FrequencySeverity + bundle with fixed output semantics and strict persistence. +- artifacts.py: public nested Model.from_record shares the existing loader checks. +- Tests/docs: matched weights, multiple rounds, policy alignment, output units + and corrupted/fresh-process inference. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_composition.py -n 0 -q: 9 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 693 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_composition.py: + passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-composition-wheel-001; + uv pip install --offline --python /tmp/openboost-composition-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp verified the installed path and executed all + docs/v1/*.md Python blocks in fresh namespaces: sixteen passed. + Build hash: [Sprint 033](../v1-sprints/033-b10-frequency-severity.md). + +## Failed Attempts + +Initial composition import failed before implementation. One unused test import +was removed by lint. + +## Risks and Follow-ups + +The helper validates supplied aggregates, not raw eligibility/joins or provenance. +It uses shared policy predictors and count-weighted positive averages; it does +not construct claim-specific covariates. Component validation selection is not +joint aggregate-quality selection. No real A9, calibration, GPU or speed claim. +AFT target/scale/output semantics are next; broader workflow gates remain open. + +## Commits + +Committed with the cohesive B10 frequency-severity slice; parent bf7ef2c. diff --git a/learnings/2026-09-06-v1-b10-gamma.md b/learnings/2026-09-06-v1-b10-gamma.md new file mode 100644 index 0000000..8cba764 --- /dev/null +++ b/learnings/2026-09-06-v1-b10-gamma.md @@ -0,0 +1,53 @@ +# 2026-09-06: Gamma means retain observation-unit weight semantics + +## Context + +B10/A8 requires positive-target means independently from Poisson frequency. +Parent dc03382; verification used this slice's dirty tree. + +## Decision or Result + +Gamma fits a positive mean with fixed unit-dispersion objective y/mean+log(mean), +original weights and explicit log-mean offsets. Claim averages with count weights +match claim-level geometry when predictors/offsets are shared within a policy. +Neither observation representation implies estimated dispersion or calibration. + +## Changes + +- objectives.py: positive support, offset-aware initialization, Gamma geometry. +- recipes.py: fixed/backtracking CPU Gamma composition and round evidence. +- outputs.py: explicit positive_mean transform. +- Tests/docs: observation units, independent multi-round and loaded inference. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_gamma.py -n 0 -q: 9 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 674 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_gamma.py: passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-gamma-wheel-001; + uv pip install --offline --python /tmp/openboost-gamma-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp verified the installed path and executed all + docs/v1/*.md Python blocks in fresh namespaces: fourteen passed. + Build hash/reflection: [Sprint 031](../v1-sprints/031-b10-gamma.md). + +## Failed Attempts + +Initial Gamma import failed before implementation. Two unused test imports +were removed by lint before final verification. + +## Risks and Follow-ups + +No estimated dispersion, calibrated distribution, real A8 quality, CUDA or speed +claim. Generic model artifacts require explicit positive_mean composition. +Tweedie/frequency-severity composition and AFT are next; all required applications +and evaluation gates remain in scope. Repeated scalar recipe scaffolding is a +possible future simplification, contingent on preserving objective semantics. + +## Commits + +Committed with the cohesive B10 Gamma slice; parent dc03382. diff --git a/learnings/2026-09-06-v1-b10-poisson.md b/learnings/2026-09-06-v1-b10-poisson.md new file mode 100644 index 0000000..3125de1 --- /dev/null +++ b/learnings/2026-09-06-v1-b10-poisson.md @@ -0,0 +1,53 @@ +# 2026-09-06: Poisson exposure is separate from sample weighting + +## Context + +B10/A7 needs count likelihood, unit-exposure rate and period count outputs. +Parent dd42967; verification used this slice's dirty tree. + +## Decision or Result + +Require positive aligned exposure and integer counts. Raw model values are log +rates; extra offsets and log exposure enter likelihood once. Original weights +apply through the existing Newton adapter. Offset-aware initialization uses log +sums; all-zero positive-weight counts use an explicit minimum_rate initializer. + +## Changes + +- objectives.py: Poisson support, stable intercept, likelihood and derivatives. +- recipes.py: scalar Poisson fixed/backtracking composition and per-round evidence. +- outputs.py: explicit named rate/count_mean transform; callers provide exposure. +- Documentation/tests: units, inference responsibility and independent oracles. + +## Verification + +Commands use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_poisson.py -n 0 -q: 10 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 665 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_poisson.py: passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-poisson-wheel-001; + uv pip install --offline --python /tmp/openboost-poisson-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp asserted the installed package path and executed + all docs/v1/*.md Python blocks in fresh namespaces: thirteen passed. + Build hash: [Sprint 030](../v1-sprints/030-b10-poisson.md). + +## Failed Attempts + +The initial Poisson import failed before implementation. Two unused test +imports were removed after lint. + +## Risks and Follow-ups + +Generic inference artifacts do not embed output-family metadata; callers retain +the named transform and supply exposure/offsets. Extreme values outside float64 +support fail explicitly. No real A7 quality, calibrated distribution, CUDA, +performance or agent-effort claim. Gamma, Tweedie/composition and AFT remain +separate required B10 work. F0.3 and F1–F5 remain incomplete. + +## Commits + +Committed with this cohesive B10 Poisson slice; parent dd42967. diff --git a/learnings/2026-09-06-v1-b10-tweedie.md b/learnings/2026-09-06-v1-b10-tweedie.md new file mode 100644 index 0000000..ff91236 --- /dev/null +++ b/learnings/2026-09-06-v1-b10-tweedie.md @@ -0,0 +1,53 @@ +# 2026-09-06: Tweedie means distinguish annualized weights from count exposure + +## Context + +B10/A9 requires nonnegative loss targets, fixed variance power and explicit +annualized/period units. Parent 373e16d; verification used this slice's dirty tree. + +## Decision or Result + +Implement zero-safe Tweedie geometry with fixed p in (1,2), offset-aware +initialization and original-weight scalar Newton rounds. Annualized targets +use exposure weights; adding log exposure again is not this contract. +The existing positive_mean transform produces the declared mean units. + +## Changes + +- objectives.py: Tweedie support/geometry and explicit all-zero initializer. +- recipes.py: fixed/backtracking rounds with per-round evidence. +- Tests/docs: three powers, zero derivatives, original exposure weights, + invalid support, rejected updates and loaded inference. + +## Verification + +Commands use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- uv run --no-sync pytest tests/v1/test_public_tweedie.py -n 0 -q: 10 passed. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 684 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_tweedie.py: passed. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-tweedie-wheel-001; + uv pip install --offline --python /tmp/openboost-tweedie-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp asserted the installed path and executed every + docs/v1/*.md Python block in fresh namespaces: fifteen passed. + Build hash: [Sprint 032](../v1-sprints/032-b10-tweedie.md). + +## Failed Attempts + +Initial Tweedie import failed before implementation. Two unused test imports +were removed by lint. + +## Risks and Follow-ups + +No normalized compound likelihood, estimated dispersion, calibrated tails, +real A9 quality, CUDA or performance claim. Frequency-severity composition and +its persisted dependencies are next, followed by AFT. Generic inference models +do not preserve evaluation power/unit metadata; complete workflow artifacts +remain required. + +## Commits + +Committed with this cohesive B10 Tweedie slice; parent 373e16d. diff --git a/learnings/2026-09-06-v1-baseline-worker.md b/learnings/2026-09-06-v1-baseline-worker.md new file mode 100644 index 0000000..6d0e6b9 --- /dev/null +++ b/learnings/2026-09-06-v1-baseline-worker.md @@ -0,0 +1,50 @@ +# 2026-09-06: Validation-only baseline worker + +## Context + +Installed comparator support does not provide the execution adapters needed by +F0.3. Count-model offsets are external to saved tree state in several libraries; +losing that state would silently change predictions after loading. + +## Decision or Result + +The numeric fixed-round worker supports the declared built-in task subset and +retains its external exposure state in a trusted local bundle. It rejects test +arrays, unknown job fields, unsupported early stopping and invalid target/weight/ +exposure/row-ID inputs. It is not yet the complete 16-trial search pipeline. + +## Changes + +- [Worker](../benchmarks/v1/baseline_worker.py): validation predictions, model bundle, + replay check before writing artifacts, explicit positive exposure at prediction. +- [Synthetic probe](../benchmarks/v1/worker_smoke.py) and + [raw result](../benchmarks/v1/evidence/worker-cpu.json): 30 CPU task/library cells. +- [Adversarial checks](../tests/v1/test_baseline_worker.py): reject silent-input paths. + +## Verification + +- `OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.worker_smoke`: + all 30 cells passed fit/reload; all three count adapters passed exposure doubling. +- `uv run --no-sync pytest tests/v1/test_baseline_worker.py -n 0 -q`: nine passed. +- Full regression suite: 431 passed, no skips; Ruff and strict MkDocs passed. + Actual comparator execution used the pinned isolated CPU environment. + +## Failed Attempts + +Review caught that unused arrays and unknown job options could be ignored. +The worker now rejects them; the synthetic fixture supplies only task-relevant +fields. NaN exposure and nonbinary censoring indicators fail before fitting. + +## Risks and Follow-ups + +The bundle uses pickle for trusted local comparator artifacts only; never load +an untrusted model with it. Replay is within the process, not the E1 independent +new-process production persistence gate. Inputs are finite pre-encoded numeric +arrays; category/missing preprocessing must be applied by the frozen caller. +Ranking, early stopping, validation-selection receipts, test unlocking, A9 composed +controls, A12 structural comparators and A13 scheduling remain incomplete. A12 here +is only its ordinary GBDT comparator. No OpenBoost model or real quality gate passed. + +## Commits + +- This slice: `eval: add validation-only baseline workers with offset replay`. diff --git a/learnings/2026-09-06-v1-bike-data.md b/learnings/2026-09-06-v1-bike-data.md new file mode 100644 index 0000000..e4926a4 --- /dev/null +++ b/learnings/2026-09-06-v1-bike-data.md @@ -0,0 +1,48 @@ +# 2026-09-06: A5 real data and full-date rolling origins + +## Context + +F0.3 needs actual data identity and split evidence, beyond a synthetic integrity +judge. A5 already specifies UCI Bike Sharing with calendar-only inputs. + +## Decision or Result + +Pin the downloaded archive/member, parsed arrays and row IDs. Split by whole +sorted dates with integer floor endpoints. The actual hour.csv has 17,379 rows +and 731 days; the UCI page says 17,389. Preserve this discrepancy in the evidence. + +## Changes + +- [Sprint 012](../v1-sprints/012-bike-data-freeze.md): scope, results and reflection. +- [Adapter](../benchmarks/v1/bike.py): calendar allowlist, count/calendar/identity + validation, no learned preprocessing or synthesized missing hours. +- [Freeze](../benchmarks/v1/datasets/bike.json): five origins, original and array + hashes, exact row counts/date bounds, source hash and honest dirty provenance. +- CLI can verify data/splits and source identity against the committed record. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.bike /tmp/openboost-v1-bike.zip --verify benchmarks/v1/datasets/bike.json`: + actual archive replay matched; corrupting a frozen test count caused nonzero exit. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 360 passed, no skips; 24 new adapter checks. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py`: pass. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict`: pass. +- macOS, Python 3.12.12, NumPy 2.3.5. No model training or test metrics inspected. + +## Failed Attempts + +- Sandboxed curl could not resolve UCI; authorized network download succeeded. +- Initial collection failed on the missing adapter; subsequent parametrized tests + needed the pytest import that an earlier unused-import cleanup had removed. + +## Risks and Follow-ups + +- Origins overlap across folds; they are not five independent experiments. +- Freeze is data preparation, not a full run manifest or budget/quality gate. +- Remaining A-ID datasets, all baseline capabilities, budgets and held-out verifiers + remain pending. Next reuse Housing hashes and finish its five split seeds. + +## Commits + +- This slice: `data: freeze A5 bike sharing inputs and rolling splits` (parent `e2f2c6b`). diff --git a/learnings/2026-09-06-v1-classification-quantile-binding.md b/learnings/2026-09-06-v1-classification-quantile-binding.md new file mode 100644 index 0000000..cab8616 --- /dev/null +++ b/learnings/2026-09-06-v1-classification-quantile-binding.md @@ -0,0 +1,47 @@ +# 2026-09-06: Classification and rolling quantile worker binding + +## Context + +The first exporter assumed each fold partitioned every source row. That fits +random/grouped partitions but contradicts the preregistered Bike rolling origins. +Adult additionally requires train-only categorical encoding and source identities. + +## Decision or Result + +A5 partitions must form a disjoint chronological source prefix with whole-date +boundaries, and exactly match the frozen row/encoding hashes. Later rows remain +unused in that origin. Other supported tasks retain complete partition coverage. +Adult preserves source/physical-line IDs; Bike preserves instant IDs. Positional +indices continue to verify preprocessing against the original freeze. + +## Changes + +- Added verified Adult, Covertype and Bike source-to-worker binding for all folds. +- Added train-only category vocabulary verification and source-ID uniqueness. +- Validate all folds before output, materializing one encoded fold at a time. +- Expanded the real worker smoke to seven application paths, with binary and + seven-class probability checks and three-column quantile schema checks. + +## Verification + +- `build/v1-env/bin/python -m benchmarks.v1.worker_data_smoke build/v1-worker-classification-quantile-001`: all 35 real-data validation fits passed across seven applications and five folds. +- [Raw summary](../benchmarks/v1/evidence/real-classification-quantile-binding-cpu.json) + retains CLI, code/data/packet hashes, environment, stopping and output identities. +- All 484 v1 tests passed, including ten binding tests. +- Ruff across production/evaluation/tests, strict MkDocs and diff whitespace checks passed. +- The worker checks model reload before emitting success. No test scores were read. + +## Failed Attempts + +The full-coverage assumption was identified before the new real-data run and +replaced with a task-specific chronological rule. No split or threshold changed. + +## Risks and Follow-ups + +A4/A7/A8/A9/A10/A13, coupled controls, complete search/test release and OS access +isolation remain open. These short CPU fits validate plumbing, not task quality +or GPU behavior. Dense intermediate packets are evaluation controls only. + +## Commits + +- This slice: `eval: bind classification and rolling quantile datasets`. diff --git a/learnings/2026-09-06-v1-classification-workers.md b/learnings/2026-09-06-v1-classification-workers.md new file mode 100644 index 0000000..0259d22 --- /dev/null +++ b/learnings/2026-09-06-v1-classification-workers.md @@ -0,0 +1,55 @@ +# 2026-09-06: Preserve classification output and external packet identity + +## Context + +Parent b430df0. Current evaluation bindings still lacked A2/A3 despite public +classification recipes. Explicit class order and packet identity are required +for meaningful log-loss/calibration and saved prediction. + +## Decision or Result + +Add binary/multiclass adapter branches using canonical encoded labels, explicit +class counts and persisted class order. Return binary positive-class probability +or all multiclass columns. Apply existing patience/best-validation selection. +At the packet boundary preserve unique external integer/string IDs and map to +local integer rows inside NumericData. Never silently replace emitted source IDs. + +## Changes + +- Current worker/inference: class schema, probability outputs, encoded-label + validation, weighted selection and external/local row separation. +- Twelve tests plus explicit A2/A3 smoke options. Default smoke set unchanged. +- [Sprint 048](../v1-sprints/048-classification-workers.md) and + [failed/passing evidence](../benchmarks/v1/evidence/classification-048/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_current_worker.py::test_binary_worker_probabilities -q -o addopts=''`: + initially failed on unsupported job. The final worker file has 32 passing cases. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 844 passed after the external-ID fix. Changed-file Ruff and strict docs pass. +- `uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-classification-048 --applications A2`: + 0/5: string source IDs rejected by NumericData. +- Rerun with `/tmp/openboost-classification-048-fixed`: 5/5 pass, exact fresh + probability replay and source row identity retained. +- macOS/Python 3.12.12/NumPy 2.3.5; one thread, 90-second fit and 30-second replay + caps. No memory cap or CUDA. A3 has synthetic verification only in this slice. + +## Failed Attempts + +All original Adult failures remain recorded. The problem was an adapter assumption, +not incompatible training mathematics. Fix at the external packet boundary instead +of expanding the core row-ID API or casting strings into invented source IDs. +String-ID tests now verify the actual consumer path. + +## Risks and Follow-ups + +Complete Covertype runs, calibration/quality search and other application adapters +remain required. D5, test isolation, real search, CUDA and author/adoption gates +are not closed by these integrations. No quality/speed or E3 claim. Nothing pushed. + +## Commits + +- This classification adapter slice; parent b430df0. diff --git a/learnings/2026-09-06-v1-comparator-capabilities.md b/learnings/2026-09-06-v1-comparator-capabilities.md new file mode 100644 index 0000000..04532f1 --- /dev/null +++ b/learnings/2026-09-06-v1-comparator-capabilities.md @@ -0,0 +1,53 @@ +# 2026-09-06: Real CPU and CUDA comparator capabilities + +## Context + +F0.3 requires installed comparator evidence before a fair quality protocol can be +frozen. Documentation alone cannot establish supported devices or persistence. + +## Decision or Result + +The isolated real-T4 matrix has 29 CPU and 28 CUDA passing built-in task cells, +with four/five explicit unsupported cells. These are synthetic fit/reload checks, +not OpenBoost capability, task quality, speed, or F0.3 completion. + +## Changes + +- [Capability probe](../benchmarks/v1/capability_smoke.py) and bounded + [Modal harness](../benchmarks/v1/modal_preflight.py), hash-locked CUDA packages. +- Weighted mixed-data/base-offset and secondary NGBoost/GLM/AFT/formula probes. +- [Raw evidence and failure index](../benchmarks/v1/evidence/README.md). + +## Verification + +- `uv run --no-sync modal run benchmarks/v1/modal_preflight.py::main`: all + supported CPU/CUDA cells passed in independent processes on a real T4. +- Pinned macOS CPU capability, adapter and secondary probes passed their declared + cases. Raw artifacts record versions and source hashes. Ruff passed. +- The source/lock hashes identify the probe bytes. The isolated result does not + record a harness digest; native build isolation dependencies are not fully pinned. + +## Failed Attempts + +The standard LightGBM wheel lacks CUDA; native compilation initially selected +missing Clang. GCC compilation succeeded. A subsequent single-process mixed-library +probe aborted in native CUDA code. Independent bounded processes preserved all +results and passed supported cells, without claiming the native root cause solved. +XGBoost reload must restore device before applying same-device tolerance. Original +cross-device deviations remain recorded; existing E1 tolerance was not changed. + +## Risks and Follow-ups + +Task semantics beyond these probes, real quality, full selection/test integration, +ranking data, unresolved licenses and independently frozen held-outs remain open. +Fresh-process isolation is required for evaluation. Public v1 training code remains +F1 work. See [Sprint 016](../v1-sprints/016-f0-3-completion.md). + +## Commits + +- This slice: `eval: verify isolated CPU and CUDA comparator capabilities`. + +Py-Boost 0.5.2 subsequently passed weighted scalar/vector MSE fit and JSON reload +on the real T4 with zero prediction differences. The first run's `verbose=0` +callback failure is retained; the corrected interval is 10. Full suite after +support changes: 431 passed, no skips; Ruff and strict documentation build passed. diff --git a/learnings/2026-09-06-v1-covertype-worker.md b/learnings/2026-09-06-v1-covertype-worker.md new file mode 100644 index 0000000..5b7f3a9 --- /dev/null +++ b/learnings/2026-09-06-v1-covertype-worker.md @@ -0,0 +1,51 @@ +# 2026-09-06: Exercise the current multiclass worker on full Covertype folds + +## Context + +Parent 0def101. Sprint 048 added A3 adapters and synthetic tests, but no current +full-dataset A3 result. The next slice executes the five frozen Covertype folds +without replacing the data with a subset or silently expanding worker budgets. + +## Decision or Result + +Use existing four-round, depth-two, 32-bin, seven-class, single-thread jobs with +90-second fit and 30-second fresh inference caps. Treat failures as evidence; +a full-fold validation smoke is not a full quality search or a speed comparison. + +## Changes + +- [Sprint 049](../v1-sprints/049-covertype-worker.md): bounded evaluation plan. +- [Raw evidence](../benchmarks/v1/evidence/covertype-049/README.md): five timeouts, + complete jobs/input identities, worker logs and bounded execution records. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-covertype-049 --applications A3`. +- Source parsing, all five frozen preprocessing records and packet identities are + checked before fitting. The source contains 581,012 rows and 54 input features; + original labels 1–7 are encoded as 0–6 by the pinned reader. +- Local macOS/Python 3.12.12/NumPy 2.3.5. No memory cap or CUDA. + +## Failed Attempts + +All five folds timed out at 90 seconds and produced no model. No predictions +were available for fresh replay, so no fold passes. Worker logs were empty. +The process records show group termination under the declared cap, not a +mathematical exception or an identified hotspot. Source/artifact hashes match. + +## Risks and Follow-ups + +Full model selection, quality/calibration, required comparator controls, D5 and +GPU/adoption gates remain open. Packet separation is not OS label isolation. +Next: profile the same full-data input with bounded phase/stack diagnostics, +separating preparation, initialization, tree construction, prediction and model +state transactions. Repeated prediction-time binning is a static hypothesis only. +Do not increase the budget or redesign caches without measurement. No code was +changed; latest CPU regression remains 844 from Sprint 048, not rerun here. +Strict MkDocs and git diff checks pass. Nothing pushed or published. + +## Commits + +- This Covertype evidence slice; parent 0def101. diff --git a/learnings/2026-09-06-v1-cpu-coverage-audit.md b/learnings/2026-09-06-v1-cpu-coverage-audit.md new file mode 100644 index 0000000..d32a457 --- /dev/null +++ b/learnings/2026-09-06-v1-cpu-coverage-audit.md @@ -0,0 +1,47 @@ +# 2026-09-06: CPU recipe breadth is not complete foundation acceptance + +## Context + +After B10/AFT, inspect actual F1/B11 readiness before beginning GPU expansion. +Audited code revision b456bf3; this slice changes documentation only. + +## Decision or Result + +The public CPU subset passes 228 tests; total suite count 716 also includes +references/evaluation infrastructure. Complete A6 regression is absent, and +train-many does not yet share binning or implement validation-driven stopping. +Ordered parameter mutation, current external author wheels and all-application +integration remain open. F1/E0/E1/E2 cannot be declared complete. + +## Changes + +- Sprint 035 maps every A1–A13/R1–R9 and C1–C7 to code/tests and remaining work. +- Prioritized A6, prepared inputs/stop isolation/M32, ordered mutations/output + dependencies, current installed author packages and real-workflow integration. +- Marked historical extension instructions at their local entry point. + +## Verification + +- Read public implementation/call paths, tests and planning contracts. +- Reproduced scalar squared rejection of two-output targets and absence of recipe + early-stopping parameters. +- UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest + tests/v1/test_public*.py -q --tb=short: 228 passed. +- Existing full suite evidence: 716 at the same production revision, Sprint 034. +- No runtime changes; no new benchmark, CUDA or real-quality evaluation. + +## Failed Attempts + +No attempted phase exit. Existing extension wheels were identified by retired +openboost.experimental imports and were not miscounted as v1 evidence. + +## Risks and Follow-ups + +The audit is an execution map, not a substitute for independent gate evaluation. +Keep all application scope and sealed held-out isolation. Next implement A6 +multi-output squared workflows, then shared preparation and independent stopping. +See [Sprint 035](../v1-sprints/035-cpu-coverage-audit.md). + +## Commits + +Committed with the CPU coverage audit; parent b456bf3. diff --git a/learnings/2026-09-06-v1-current-worker.md b/learnings/2026-09-06-v1-current-worker.md new file mode 100644 index 0000000..d44ef4c --- /dev/null +++ b/learnings/2026-09-06-v1-current-worker.md @@ -0,0 +1,56 @@ +# 2026-09-06: Connect the current foundation to evaluation workers + +## Context + +Parent 03ff34e. Sprint 038 M3 called for current OpenBoost integration; the existing +baseline worker only executed incumbents. Further isolated objectives would not +resolve this evidence gap. + +## Decision or Result + +Add a separate current worker and inference entry point rather than importing +current recipe policy into incumbent branches. A1 and A11 consume frozen numeric +packets with explicit validation targets. Fixed budgets return final models; +patience returns the strict best validation snapshot. Persist output semantics +with the raw model: scalar means or Normal means/standard deviations. The bundle +is benchmark-specific JSON, not a stable new public API. + +## Changes + +- openboost_worker.py: schema-checked current CPU trial adapter. +- openboost_predict.py: declared output decoding without recipe imports. +- openboost_worker_smoke.py: bounded five-fold real validation replay. +- Fifteen focused tests; [Sprint 044](../v1-sprints/044-current-worker.md) and + [raw evidence](../benchmarks/v1/evidence/current-worker-044/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- Initial `uv run --no-sync pytest tests/v1/test_current_worker.py -q -o addopts=''` + failed on the missing worker. After implementation fifteen focused cases pass. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 814 passed. Changed-file Ruff and strict MkDocs pass. +- `uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-current-worker-044`: + 10/10 A1/A11 five-fold validation cells pass, each with exact fresh-process replay. +- Recorded source and raw worker artifact hashes match committed files. +- macOS x86_64/Python 3.12.12/NumPy 2.3.5; one thread per real worker, + 90-second fit limits and 30-second inference limits. No memory cap or CUDA. + +## Failed Attempts + +The initial absence was reproduced. No core code change was needed. Validation +packets are supplied explicitly; the adapter does not invent dummy labels or +silently skip best-model/stopping semantics. + +## Risks and Follow-ups + +Four rounds/32 bins validate integration only. All A1–A13 remain required; A6/A13 +scaling/selection and other adapters are next. Full search, held-out test scoring, +quality comparison and OS test-label isolation are still absent. Packet files +are an interface boundary, not a security boundary. Formal F0.3/E3/E6 remain open. +Remaining D5 development also remains open. Nothing pushed or published. + +## Commits + +- This current-worker slice; parent 03ff34e. diff --git a/learnings/2026-09-06-v1-early-stopping.md b/learnings/2026-09-06-v1-early-stopping.md new file mode 100644 index 0000000..9fb4fc9 --- /dev/null +++ b/learnings/2026-09-06-v1-early-stopping.md @@ -0,0 +1,67 @@ +# 2026-09-06: Baseline validation stopping and selected-iteration replay + +## Context + +The search design declares patience 50, but baseline workers rejected stopping. +Native APIs differ in whether prediction defaults include trees after the best +iteration. A successful fit alone cannot verify saved-model selection semantics. + +## Decision or Result + +Require explicit validation targets and support nonunit validation weights and +A10 censoring. Use pinned native stopping metrics within trials, with independent +prediction-space metrics retained for cross-method selection. Save prediction +limits for XGBoost/NGBoost; preserve LightGBM's per-model best iteration and +CatBoost's truncated model. Count validation receives explicit exposure offsets. + +## Changes + +- [Worker](../benchmarks/v1/baseline_worker.py): explicit validation contract, + native callbacks/validation pools, selected prediction limits and training JSON. +- [Stopping smoke](../benchmarks/v1/early_stopping_smoke.py) extends the weighted + worker matrix and tests new-process reload on overfitting counterexamples. +- [Selection smoke](../benchmarks/v1/selection_smoke.py) accepts an optional + stopping patience; this setting participates in its protocol identity. +- Input tests reject invalid patience, missing targets, invalid validation weights + and unused validation fields when stopping is disabled. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_baseline_worker.py -n 0 -q`: 15 passed. +- `OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.early_stopping_smoke`: + 30 CPU task/library cells passed; selected iterations match validation-history + minima. XGBoost, LightGBM, CatBoost scalar and NGBoost distribution fixtures + select round 1 before the final trained round and replay in new processes. +- [Raw stopping histories](../benchmarks/v1/evidence/early-stopping-cpu.json) + identify code and the locked CPU environment. Patience three/max 24 rounds are + smoke settings, not changes to preregistered real-search budgets. + +## Failed Attempts + +Inspection of the pinned source showed XGBoost and NGBoost retaining extra trees +by default. Explicit selected-round prediction avoids silently evaluating the +last trained ensemble. The earlier worker's rejection of stopping remains an +accurate historical boundary, superseded by this slice. + +## Risks and Follow-ups + +Native stopping metrics differ by method; do not describe this as a universal +custom-metric stopping implementation. Validation-selected independent task +metrics determine the opponent. GPU stopping remains unverified. Full real-task +matrix/worker isolation, ranking, composed/structured controls, auxiliary scores, +licenses and held-outs still prevent F0.3 exit. + +## Commits + +- This slice: `eval: support native baseline early stopping and replay`. + +Final checks: 449 tests passed with no skips; Ruff and strict MkDocs passed. +The 16-trial selection smoke also passed with stopping enabled, including CLI +training metadata and selected-model new-process inference. Its +[summary](../benchmarks/v1/evidence/selection-early-stopping-cpu.json) records hashes; +full generated artifacts remain under ignored build output. Source hashes in both +new evidence files match the committed implementation bytes. + +The unchanged fixed-round mode also passed all 30 CPU cells using +`OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -c 'from benchmarks.v1.worker_smoke import run; print(len(run()))'`; +this rerun did not overwrite the historical fixed-round artifact. diff --git a/learnings/2026-09-06-v1-evaluation-machinery.md b/learnings/2026-09-06-v1-evaluation-machinery.md new file mode 100644 index 0000000..def50b9 --- /dev/null +++ b/learnings/2026-09-06-v1-evaluation-machinery.md @@ -0,0 +1,51 @@ +# 2026-09-06: Independent scoring and bounded evaluation workers + +## Context + +F0.3 needs independent quality decisions, leakage-resistant preprocessing, and +workers whose crashes/timeouts remain failures. See [Sprint 016](../v1-sprints/016-f0-3-completion.md). + +## Decision or Result + +Add separate layers for train-only encoding, raw prediction metrics, five-fold +comparisons, strict validation-only trial selection, hashed row-aligned artifacts, +and bounded process execution. Do not call these layers a completed E3 judge: +selection receipts and complete recipe/device coverage still require integration. + +## Changes + +- [Preprocessing](../benchmarks/v1/preprocessing.py) and actual five-split freezes; + insurance claim and aggregate cases fit their own training populations. +- [Quality](../benchmarks/v1/quality.py), [artifact comparisons](../benchmarks/v1/quality_report.py), + and [process execution](../benchmarks/v1/process_runner.py). +- [Search design](../benchmarks/v1/search-design.json): 16 configurations per listed + comparator family, explicit CPU/GPU/time/retry limits; not a completed run manifest. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_quality_evaluation.py tests/v1/test_quality_artifacts.py tests/v1/test_process_runner.py tests/v1/test_evaluation_preprocessing.py -n 0 -q`: + 17 passed. Tests cover failed/missing folds, per-target errors, NLL/censoring, + invalid probabilities, row permutation after rehashing, timeout/nonzero exit, + missing artifacts, unseen categories, and event/censor ties. +- Actual preprocessing freeze and replay passed using the pinned CPU environment. +- Current full suite: 422 passed, no skips. Ruff and strict MkDocs build passed. + macOS/Python 3.12.12. No real model-quality or production-device claim. + +## Failed Attempts + +- Initial imports failed before quality and artifact modules existed. +- The live comparator probe later demonstrated that native GPU aborts cannot be + caught as Python exceptions; independent process boundaries are mandatory. + +## Risks and Follow-ups + +- The process runner enforces wall time/threads, not a process-tree memory cap. + Container limits and their provenance must be wired into full task execution. +- Quality comparison output intentionally leaves E3 false. A13 selection receipts, + full expected matrix, frozen agent cohort and unseen H1/H2 remain required. +- AFT primary censored NLL is implemented. Independent test-support-aware IPCW + Brier/C-index integration and classification auxiliary reports remain pending. + +## Commits + +- This slice: `eval: add independent scoring preprocessing and bounded workers`. diff --git a/learnings/2026-09-06-v1-expectile-extension.md b/learnings/2026-09-06-v1-expectile-extension.md new file mode 100644 index 0000000..3189355 --- /dev/null +++ b/learnings/2026-09-06-v1-expectile-extension.md @@ -0,0 +1,54 @@ +# 2026-09-06: Author an expectile objective outside the foundation + +## Context + +Sprint 038 M2 still required D1 public objective authoring. Parent b7fdb60. +D1 is intentionally an incumbent-friendly control, not an assumed advantage. + +## Decision or Result + +A separate ob-expectile wheel implements analytic geometry, weighted initialization +and a small loop entirely through public operations. No production changes were +needed. Initialization uses bisection, independently checked by stationary-interval +enumeration. Unweighted derivatives enter newton, which applies sample weights +once. Offsets enter geometry and initialization but are excluded from raw artifacts. + +## Changes + +- [Package](../examples/v1_extensions/expectile/README.md), independent trace + generator, installed checker and five focused source tests. +- Installed verifier now builds five wheels and removes four training plugins + before checking nine saved raw models in another interpreter. +- [Sprint 043](../v1-sprints/043-expectile-extension.md) and + [raw evidence](../benchmarks/v1/evidence/expectile-043/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_public_expectile.py -q -o addopts=''`: + initial test failed because the package did not exist; implementation resolves it. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 799 passed, including five new checks. +- `uv run --no-sync ruff check src/openboost examples/v1_extensions tests/v1/test_public_expectile.py`: + passed; `uv run --no-sync mkdocs build --strict`: passed. +- `uv run --no-sync python examples/v1_extensions/verify.py /tmp/openboost-v1-expectile-043`: + all installed extension, scheduling and plugin-free inference checks passed. +- macOS x86_64/Python 3.12.12/NumPy 2.3.5; no CUDA or real-data claims. + +## Failed Attempts + +The initial missing-module failure establishes the absent extension, not a +foundation limitation. No mathematical mismatch or core revision was required. + +## Risks and Follow-ups + +Loop wiring remains author-owned and duplicated; do not introduce a general +trainer without further evidence. The fixed-step example has no line search and +claims only its explicit signature. Raw prediction callers supply offsets. +Continue remaining D5 development and real worker integration. Formal E2/E5/E6, +quality/cost, CUDA and adoption remain open. No push or publication. + +## Commits + +- This D1 extension and verification slice; parent b7fdb60. diff --git a/learnings/2026-09-06-v1-goal-progress-review.md b/learnings/2026-09-06-v1-goal-progress-review.md new file mode 100644 index 0000000..b82c631 --- /dev/null +++ b/learnings/2026-09-06-v1-goal-progress-review.md @@ -0,0 +1,65 @@ +# 2026-09-06: Recenter execution on verified algorithm changes + +## Context + +The user requested a review of the goal, progress and planning after shared CPU +preparation. Reviewed clean revision `8afce35`. Sprint 035's audit was partly +superseded by multi-output recipes/scaling and M=1/8/32 preparation reuse, while +several current-status paragraphs still described early B03 or missing AFT code. + +## Decision or Result + +The CPU implementation now has substantial algorithm breadth and shared semantic +components. That supports further testing of the foundation hypothesis; it does +not establish lower independent authoring cost, real quality, GPU cost or adoption. +The next correctness slice is independent validation-driven stopping. Installed +public extension trials and current OpenBoost real-data integration should follow +early, rather than another sequence of built-in objectives. + +Preserve every required family and E0–E7 threshold. F0.3 and formal F1–F5 remain +open. The CPU overlap does not authorize interface freeze or formal comparisons +without their explicit prerequisite ledgers. Keep exploratory authoring separate +from E5 and external adoption; keep held-out contents outside foundation design. + +## Changes + +- [Sprint 038](../v1-sprints/038-goal-progress-and-plan.md): goal/progress review, + complete application status, six dependency groups, acceptance and immediate work. +- [Agent guide](../AGENTS.md), [sprint index](../v1-sprints/README.md) and + [main plan](../planning/agent-boosting-foundation-plan.md): current execution + pointers and status corrected without rewriting historical evidence or gates. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/v1/test_public*.py --collect-only -q -o addopts=''` + collected 247 public tests. This is collection, not a new regression pass. +- Last full regression: 735 passed, recorded in + [Sprint 037](../v1-sprints/037-shared-preparation.md), with 19 installed-wheel + examples, lint, strict documentation build and packaging on macOS/Python 3.12.12/ + NumPy 2.3.5. No broader environment or performance claim. +- Checked relative Markdown targets in all five changed files with pathlib; + `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict` + and `git diff --check` passed. Documentation-only change; no fresh model fits. + +## Failed Attempts + +No implementation attempt in this review. The audit found stale status text; +historical sprint findings must be interpreted at their recorded revisions. +Test counts and preparation equivalence cannot be promoted into product gates. + +## Risks and Follow-ups + +- Private recipe helpers and repeated policy loops may obstruct independent + authoring; actual D2/D3/D4 trials must determine the minimum public changes. +- Prediction recomputation may dominate cost and impede device residency. This + is a call-path risk, not a measured regression. Profile before cache redesign. +- Close global evaluation matrix, source/protocol gaps and current OpenBoost + worker integration. Use the Sprint 017 ledger rather than repeating baseline smokes. +- GPU and independent repeated adoption remain unverified. Do not push or contact + external authors without the user's explicit authorization. + +## Commits + +- This review commit — review goal and evidence; order remaining v1 acceptance work. +- `654a3f2` — shared preparation implementation reviewed here. +- `8afce35` — audited parent, including its whitespace correction. diff --git a/learnings/2026-09-06-v1-heldout-seal.md b/learnings/2026-09-06-v1-heldout-seal.md new file mode 100644 index 0000000..cd0c851 --- /dev/null +++ b/learnings/2026-09-06-v1-heldout-seal.md @@ -0,0 +1,55 @@ +# 2026-09-06: Evaluation-side local held-out seal + +## Context + +Sprint 016 requires H1/H2 cards and mathematical/state verifiers outside the +foundation designer's context. The user authorized a separate evaluator agent. + +## Decision or Result + +An evaluator agent authored and locally sealed the package. The public +[manifest](../benchmarks/v1/heldout-manifest.json) contains only hashes, storage +paths, validation status and independence/storage limitations. No task or verifier +contents were sent to the foundation designer. This is a preparation artifact, +not an author-evaluation result or closure of F0.3. + +## Changes + +- Public manifest: exact package and member SHA-256 values, local custody path, + validation status, pending execution gates and independence limitations. +- Private package: locally frozen evaluation material; do not inspect it during + interface design. Preserve the private bytes before temporary-directory cleanup. + +## Verification + +- Internal mathematical/state verifier self-validation: passed. +- Diagnostic candidate entry point and adversarial output rejection: passed. +- Invalid-parameter rejection: passed. +- Ruff check of both private Python support files: passed. +- Archive member hashes and public manifest consistency: verified locally. +- Exact commands and environment are retained inside the sealed package. This + record deliberately omits content-bearing details. + +## Failed Attempts + +- Initial uv invocation could not access its default sandboxed cache. Repeating + with a dedicated temporary cache succeeded; no environment packages changed. + +## Risks and Follow-ups + +- Independence is a separate agent context on the same system and filesystem, + not an independent human, independently selected model cohort, or OS security + boundary. Public development material was visible to the evaluator. +- Read-only file modes prevent accidental edits, not access by the parent or + future candidates. The archive has been copied opaquely and hash-verified into ignored + `build/v1-heldout/package.tar`; this avoids temporary cleanup but still requires + private preservation before build cleanup. It is not backed up. +- Real library integration, actual fresh-process persistence, comparator and + cohort freeze, restricted execution, and E5 attempts remain pending. Numerical + diagnostic success does not satisfy these gates. +- If task contents inform foundation design, retire the affected held-out and + replace it before evaluating unseen-task performance. + +## Commits + +- This slice: `eval: seal independently authored held-out package metadata`. diff --git a/learnings/2026-09-06-v1-housing-data.md b/learnings/2026-09-06-v1-housing-data.md new file mode 100644 index 0000000..6537c4d --- /dev/null +++ b/learnings/2026-09-06-v1-housing-data.md @@ -0,0 +1,48 @@ +# 2026-09-06: Housing legacy identity and five v1 splits + +## Context + +A1/A11 require the same California Housing inputs, but the historical record only +contains seeds 0–2. F0.3 must verify those and add seeds 3–4. + +## Decision or Result + +The independent v1 adapter reproduces the archived X/y byte hash and all nine old +split hashes. Five seeds now have committed hashes. Keep little-endian float32 and +the historical raw-byte hash convention, explicitly distinguished from Bike hashes. + +## Changes + +- [Sprint 013](../v1-sprints/013-housing-five-splits.md) records the plan/reflection. +- [Housing adapter](../benchmarks/v1/housing.py) validates raw shape, finite values, + positive household denominators and float32 overflow; uses isolated seeded RNG. +- [Freeze](../benchmarks/v1/datasets/housing.json) binds all five splits and source. + A1/A11 remain distinct quality tasks but one data source. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.housing build/foundation_data/cal_housing.tgz --verify benchmarks/v1/datasets/housing.json`: + real-data replay matched; corrupted seed4 hash caused nonzero exit. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 380 passed, no skips, including 20 new adapter tests. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py`: pass. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict`: pass. +- macOS/Python3.12.12/NumPy2.3.5; no training or test metrics. + +## Failed Attempts + +- Preimplementation collection failed on the absent module; early lint rejected + a semicolon-separated test line, subsequently split by formatting. +- Original source webpage timed out. Archive contains no license file; license + stays unresolved rather than inferred from download availability. + +## Risks and Follow-ups + +- Random splits do not establish geographic generalization. Data identity does not + establish predictive quality, and unresolved licensing remains an F0.3 item. +- Next Adult official-test/stratified training splits; remaining required datasets, + baseline capabilities, budgets, held-out tasks and runner still pending. + +## Commits + +- This slice: `data: freeze A1 A11 housing with five splits` (parent `dd2e9ad`). diff --git a/learnings/2026-09-06-v1-independent-stopping.md b/learnings/2026-09-06-v1-independent-stopping.md new file mode 100644 index 0000000..4369b4e --- /dev/null +++ b/learnings/2026-09-06-v1-independent-stopping.md @@ -0,0 +1,70 @@ +# 2026-09-06: Validation patience is separate from model acceptance + +## Context + +Sprint 038 M1 identified that best-model retention and different fixed budgets +did not establish independent stopping. Parent 7a61744. This slice executes +[Sprint 039](../v1-sprints/039-independent-stopping.md) without changing phase gates. + +## Decision or Result + +Public immutable StopState owns the round budget, optional patience, threshold +reference and progress. Recipes observe current validation once after an outer +round, including full rejection. Search trials do not consume patience. Strict +best-model selection remains independent of the threshold used to reset patience. +RunSpec passes scalar policy options through its existing public contract. + +Independent scans of fixed-budget scalar/Normal fits determine expected stopping +prefixes. M=1/8/32 shared-preparation executions preserve those prefixes, best +models and stop records under reversal, regrouping and retry, with failures retained. + +## Changes + +- stopping.py: public finite-score/budget/patience state and immutable observation. +- recipes.py: all twelve recipes observe stopping and return FitResult.stop. +- Tests: thirty new cases, including all recipes, rejection, nonfinite failures, + independent metric-sequence oracles and heterogeneous shared-run equivalence. +- Public docs, sprint index and agent guidance: implemented scope and next work. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_public_stopping.py -q -o addopts=''`: + initial missing-module failure; 29 passed after correcting the Normal fixture. + The added numerical failure-isolation case passes in the full regression. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 765 passed, including all thirty new tests. +- `uv run --no-sync ruff check src/openboost tests/v1/test_public_stopping.py`: + passed. Formatted changed Python files; no unrelated production cleanup. +- `uv run --no-sync mkdocs build --strict`; `uv build --offline`: passed. +- `uv venv --python .venv/bin/python /tmp/openboost-stopping-wheel-001`; + `uv pip install --offline --python /tmp/openboost-stopping-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5`: passed. +- Isolated Python `-I` subprocesses from /tmp verified site-packages imports and + ran all twenty docs/v1 Python examples in fresh namespaces: passed. +- Environment: macOS, Python 3.12.12, NumPy 2.3.5. No CUDA or other OS verification. + +Tested the parent's working tree plus this slice. Local wheel SHA256: +efc7705eb2cde914e4f1445cb087904bee02552df369b270728d7f80f4f7ec01. + +## Failed Attempts + +- The first focused test could not import StopState before implementation. +- The all-recipe fixture incorrectly supplied Formula structure to Normal. + Normal correctly rejected it; fixed the fixture without loosening validation. +- Ruff required explicit non-strict zip for adjacent outcome pairs and import + ordering; corrected before final lint/regression. + +## Risks and Follow-ups + +Stopping is an in-memory record, not a resume checkpoint. Recipe candidate +numerical rejection semantics remain unchanged. Validation observation evaluates +the metric over existing raw predictions; no end-to-end performance claim follows. +Next: installed public D2/D3 extension trials, D4 ordered updates and M3 real-data +integration under Sprint 038. No GPU, external adoption or complete E-gate claim. + +## Commits + +- This implementation commit — independent validation stopping across CPU recipes. +- `7a61744` — parent goal/progress review and execution plan. diff --git a/learnings/2026-09-06-v1-installed-extensions.md b/learnings/2026-09-06-v1-installed-extensions.md new file mode 100644 index 0000000..7149e46 --- /dev/null +++ b/learnings/2026-09-06-v1-installed-extensions.md @@ -0,0 +1,65 @@ +# 2026-09-06: Public callbacks support installed D2/D3 extensions + +## Context + +Sprint 038 M2 calls for testing actual public authoring boundaries before adding +more built-ins. Parent 2f6c77a. Historical extension wheels import retired APIs +and cannot establish current v1 support. + +## Decision or Result + +Two new development wheels implement independent cohort feasibility and penalized +leaves using public callbacks. Neither requires core edits, private imports or +recipe-loop copying. The core wheel is byte-identical to Sprint 039's wheel. +Installed checks validate split choice, leaf mathematics, subsequent updates and +plugin-free inference. These are repository-authored exploratory trials, not an +independent author cohort, full E2/E6 or measured E5 advantage. + +## Changes + +- examples/v1_extensions/: two packages, independent oracle checks, isolated + build/install/remove/inference verifier and usage/limitations. +- tests/v1/test_public_extensions.py: source development oracle and import checks, + explicitly distinguished from installed evidence. +- [Raw artifacts](../benchmarks/v1/evidence/installed-extensions-040/README.md): + hashes, environment, commands, outputs and persisted models. +- [Sprint 040](../v1-sprints/040-installed-extensions.md), public docs and execution + pointers record the bounded result and remaining work. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_public_extensions.py -q -o addopts=''`: + two passed, including the final routed-leaf strengthening. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 767 passed before that strengthening; affected focused checks passed afterward. +- `uv run --no-sync ruff check src/openboost examples/v1_extensions tests/v1/test_public_extensions.py`: + passed; `uv run --no-sync mkdocs build --strict`: passed. +- `uv run --no-sync python examples/v1_extensions/verify.py /tmp/openboost-v1-extension-evidence-040-routed`: + three offline wheel builds, fresh environment installation, isolated `-I` + execution, two plugin uninstalls and exact fresh-process core inference passed. +- Thirty deterministic D3 oracle comparisons have maximum absolute error + 8.881784197001252e-16. D2 constrained cut 1 is selected by all three growers. +- Platform: macOS x86_64, Python 3.12.12, NumPy 2.3.5, one BLAS/OpenMP thread. + No timing, GPU, broader-platform or real-data claim. + +## Failed Attempts + +- Initial imports failed because the new packages did not exist yet. +- The checker initially used `TreeTerm.tree`; the public field is `learner`. + Corrected the checker, with no core compatibility shim or API change. +- Initial D3 integration only checked a root leaf; strengthened it to routed + multi-leaf trees before recording final installed evidence. + +## Risks and Follow-ups + +Leaf replacement currently needs a grower adapter and duplicate quantile settings. +The package owns penalty/anchor; recipe defaults must remain unused by the custom +solver. This is documented authoring friction, not measured task-cost advantage. +Next D4 ordered updates, D1/D5 installed probes and real worker integration; preserve +held-out separation and every required application. No push or publication. + +## Commits + +- This development-extension commit; parent `2f6c77a`. diff --git a/learnings/2026-09-06-v1-multioutput-quality.md b/learnings/2026-09-06-v1-multioutput-quality.md new file mode 100644 index 0000000..dfd927e --- /dev/null +++ b/learnings/2026-09-06-v1-multioutput-quality.md @@ -0,0 +1,50 @@ +# 2026-09-06: Report standardized A6 quality without hiding target failures + +## Context + +Parent 8d8272e. Selection now binds normalization to training data, but paired +quality reports still omitted the standardized-average metric required by A6. + +## Decision or Result + +Require hashed training row IDs, targets and scale for A6 quality cells. Recompute +population scaling and reject row overlap/misalignment or inconsistent metadata. +Report mean standardized RMSE alongside every original-unit target RMSE. Apply +paired comparison thresholds to each metric; an average never overrides a failed +target. Keep E3_pass false because selection provenance/full coverage are external. + +## Changes + +- quality_report.py: verified A6 normalization support and standardized metric. +- Five artifact tests including a better average with a failing target, constant + target scaling and rejected missing/forged/evaluation-fitted scale support. +- [Sprint 047](../v1-sprints/047-multioutput-quality.md) and benchmark documentation. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_quality_artifacts.py::test_a6_reports_standardized_average -q -o addopts=''`: + after correcting a fixture helper NameError, failed before implementation with + unsupported primary metrics. The completed focused artifact file passes. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 832 passed. Changed-file Ruff and strict MkDocs pass. +- CPU macOS/Python 3.12.12/NumPy 2.3.5. No real-data, GPU or timing evaluation. + +## Failed Attempts + +Initial fixture helper was scoped incorrectly and fixed before reproducing the +actual report gap. Lint also caught an unbound closure loop variable; the fixture +binds its fold explicitly. Neither was a foundation correctness finding. + +## Risks and Follow-ups + +Hashed supplied training data still requires trusted source/protocol provenance. +The report does not certify model selection, OS label isolation or complete v1 +coverage. Actual paired A6 quality and other application adapters/searches remain +open. Sprint 017's normalization implementation gap is closed, not its full +F0.3 ledger. D5, CUDA and external author/adoption work remain. Nothing pushed. + +## Commits + +- This A6 quality-report slice; parent 8d8272e. diff --git a/learnings/2026-09-06-v1-multioutput-worker.md b/learnings/2026-09-06-v1-multioutput-worker.md new file mode 100644 index 0000000..7dbe675 --- /dev/null +++ b/learnings/2026-09-06-v1-multioutput-worker.md @@ -0,0 +1,55 @@ +# 2026-09-06: Preserve the frozen scale convention in A6 workers + +## Context + +Parent 374ab87. A6 remained unsupported in the current evaluation worker. The +public TargetScale.fit uses weights; the frozen A6/comparator convention uses +unweighted training-population means and standard deviations. + +## Decision or Result + +Use the frozen preprocessing operation on training targets only and construct a +public TargetScale from its output. Train/validation objectives use the same +standardized units. Persist the scale with the model and inverse-transform once +through MultiOutputModel. Weights still affect objective derivatives and losses; +they do not silently redefine the frozen normalization convention. + +## Changes + +- Current worker and inference: matrix target schema, shared/independent recipes, + validated scale metadata and original-unit output. +- Smoke: selectable applications, A6 freeze equality, five grouped folds. +- [Sprint 045](../v1-sprints/045-multioutput-worker.md), five new tests and + [raw evidence](../benchmarks/v1/evidence/multi-worker-045/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- Before editing, `uv run --no-sync pytest tests/v1/test_current_worker.py::test_a6_train_scale_and_original_units -q -o addopts=''` + failed with unsupported job. After changes the full worker file has 20 passes. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 819 passed. Changed-file Ruff and strict MkDocs pass. +- `uv run --no-sync python -m benchmarks.v1.openboost_worker_smoke /tmp/openboost-multi-worker-045-final --applications A6`: + 5/5 real shared-tree cells pass, exact freeze-scale equality and fresh replay. +- Source and raw worker artifact hashes match final files. macOS x86_64, + Python 3.12.12, NumPy 2.3.5, one thread, 90-second fit/30-second replay caps. + +## Failed Attempts + +A6 rejection was reproduced before the adapter change. No core changes or frozen +metadata revisions were required. Final review corrected a metric label: the +recipe averages over rows and sums over channels. The real artifact run was +repeated after correcting that label; selected models did not change. Reusing weighted fit directly would have changed +the declared evaluation convention; the adapter explicitly avoids that substitution. + +## Risks and Follow-ups + +Real runs use shared mode; independent mode has synthetic direct/fresh parity. +Four-round/32-bin validation checks are not quality or cost measurements. A13 +cross-configuration selection, all remaining adapters, D5, formal label isolation, +full search/test scoring and F0.3 remain open. Nothing pushed or published. + +## Commits + +- This A6 worker slice; parent 374ab87. diff --git a/learnings/2026-09-06-v1-ordered-updates.md b/learnings/2026-09-06-v1-ordered-updates.md new file mode 100644 index 0000000..bcd0db8 --- /dev/null +++ b/learnings/2026-09-06-v1-ordered-updates.md @@ -0,0 +1,71 @@ +# 2026-09-06: Ordered updates compose; external result interoperability remains open + +## Context + +Sprint 038 M2/D4 requires changed parameter order and adaptive acceptance through +public operations. Parent 6b2385f. The built-in Normal and Formula recipes update +both channels jointly; more built-ins alone would not establish external authoring. + +## Decision or Result + +A separately installed package composes Normal ordinary/Fisher and Formula full +GGN through the same objective-independent ordered loop. Each parameter observes +the latest accepted state, with six bounded rates and finite strict descent. +StopState advances once per outer round; validation chooses best snapshots after +each accepted parameter. No core edits or private imports were necessary. + +run_many rejects the extension's OrderedResult despite matching run/problem state. +This is a concrete shared-result abstraction gap, not an algorithm failure. Keep +it as a D5 follow-up before claiming complete E2/CPU expressiveness. Do not add a +special-case scheduler branch or count this rejected run as successful scheduling. + +## Changes + +- examples/v1_extensions/ordered_updates/: installed public ordered-sweep package. +- ordered_oracle.py / ordered_checks.py: separate reference generation and isolated + installed comparison, including the scheduler counterexample. +- verify.py / core_inference.py: four-wheel builds and eight raw model roundtrips + after uninstalling all three plugins; reference/source/artifact hashes retained. +- tests/v1/test_public_ordered.py: sixteen ordered/rejection/stopping checks. +- [Sprint 041](../v1-sprints/041-ordered-updates.md) and + [raw evidence](../benchmarks/v1/evidence/ordered-updates-041/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_public_ordered.py -q -o addopts=''`: + fourteen passed before adding partial-rejection/recovery cases; all sixteen + pass in the full regression. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 783 passed. +- `uv run --no-sync ruff check src/openboost examples/v1_extensions tests/v1/test_public_ordered.py`: + passed; `uv run --no-sync mkdocs build --strict`: passed. +- `uv run --no-sync python examples/v1_extensions/verify.py /tmp/openboost-v1-ordered-041`: + installed development checks and eight fresh-process raw roundtrips pass. + Maximum installed raw/reference difference: 2.220446049250313e-16. +- macOS x86_64, Python 3.12.12, NumPy 2.3.5, one BLAS/OpenMP thread for installed + checks. Core wheel matches Sprint 039. No CUDA, quality or performance result. + +## Failed Attempts + +- Initial extension import failed before implementation. +- The first reference comparison used the reference helper's default step sizes, + while the extension correctly used D4's fixed six rates. Bound rates explicitly. +- A depth-one extension learner then disagreed with D4's depth-two reference; + aligned the extension default to the declared reference task. No oracle or + tolerance relaxation was used to mask those configuration differences. +- Installed run_many rejects OrderedResult; preserved in raw evidence and next plan. + +## Risks and Follow-ups + +Substep diagnostic records retain full immutable before/after states and are not +memory optimized or resumable checkpoints. Geometry's returned loss and the search +loss callback must describe the same objective. Raw inference passing does not +complete Normal/Formula output/dependency or real-data workflows. Next D5 result +interoperability, D1/D5 installed probes and real worker integration. E5/E7 remain +unverified; no push, publication or external contact. + +## Commits + +- This development slice; parent `6b2385f`. diff --git a/learnings/2026-09-06-v1-parametric-workers.md b/learnings/2026-09-06-v1-parametric-workers.md new file mode 100644 index 0000000..1dc950d --- /dev/null +++ b/learnings/2026-09-06-v1-parametric-workers.md @@ -0,0 +1,50 @@ +# 2026-09-06: Parametric and paid-loss comparison workers + +## Context + +A9 requires a matching paid-frequency/severity baseline, not raw ClaimNb multiplied +by positive-only severity. A12 requires the same global formula as a control. +Earlier mathematical probes did not provide runnable validation worker adapters. + +## Decision or Result + +Add positive-family GLMs, a paid-record-validated composition and positive global +formula fit. Business/exposure weights enter once with explicit output units. +Training-fitted scaling is saved. Invalid joins and unsuccessful convergence fail. + +## Changes + +- [Controls](../benchmarks/v1/parametric.py) and strict + [worker](../benchmarks/v1/parametric_worker.py). +- [Hand-check smoke](../benchmarks/v1/parametric_smoke.py) and + [subprocess smoke](../benchmarks/v1/parametric_worker_smoke.py). +- Six mathematical/input counterexamples and 16 preregistered composition penalty + pairs; no real quality data was used to choose their range. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_parametric_controls.py -n 0 -q`: six passed. +- Pinned CPU `python -m benchmarks.v1.parametric_smoke`: five controls passed + independent constant-feature weighted-mean formulas, exposure doubling and + persistence; the nonlinear formula recovered known parameters. +- Pinned CPU `python -m benchmarks.v1.parametric_worker_smoke build/v1-parametric-workers-001`: + all five workers passed bounded CLI execution and prediction/row-ID checks. +- [Raw control evidence](../benchmarks/v1/evidence/parametric-cpu.json) and + [worker evidence](../benchmarks/v1/evidence/parametric-worker-cpu.json). +- Full suite: 460 passed, no skips. Ruff and strict MkDocs passed. + +## Failed Attempts + +No failed numerical fit in this slice. Explicit hand-worked count/total mismatches +and orphan/nonpositive claims fail before either composition model fits. + +## Risks and Follow-ups + +These are CPU benchmark controls, not OpenBoost production models. Pickle bundles +are for trusted local artifacts only. Real task/source/search binding, outer coupled +controls, auxiliary scores and held-out work remain open. Global formula recovery +on an identifiable synthetic curve is not real-data parameter identification. + +## Commits + +- This slice: `eval: add parametric and paid-loss comparison workers`. diff --git a/learnings/2026-09-06-v1-positive-survival-binding.md b/learnings/2026-09-06-v1-positive-survival-binding.md new file mode 100644 index 0000000..7321d37 --- /dev/null +++ b/learnings/2026-09-06-v1-positive-survival-binding.md @@ -0,0 +1,49 @@ +# 2026-09-06: Counts, paid amounts and survival worker packets + +## Context + +Insurance application populations differ despite sharing policy partitions. +Poisson exposure offsets and Tweedie exposure weights cannot be interchanged. +Survival requires event state and a training-only censoring distribution. + +## Decision or Result + +Map policy partitions to retained positive claims or eligible policies before +using their frozen encoders. A7 receives period counts plus exposure offsets; +A8 receives individual payment amounts and unit weights; A9 receives annualized +paid totals and one exposure weight. A10 exports events and the frozen training G. + +## Changes + +- `worker_data.py`: application population binding, weights/offset contracts, + period artifacts, event arrays and hashed censoring support JSON. +- `worker_data_smoke.py`: selectable applications, positive-mean and AFT output + checks; all executed commands remain recorded. +- Four hand-worked tests cover shared policy splits, individual claim units, + annualization/weighting, offset inputs and changed censoring support rejection. + +## Verification + +- `build/v1-env/bin/python -m benchmarks.v1.worker_data_smoke build/v1-worker-positive-survival-001 --applications A7 A8 A9 A10`: all 20 real-data CPU validation fits passed. +- [Raw summary](../benchmarks/v1/evidence/real-positive-survival-binding-cpu.json) + retains actual command, source/data/packet hashes, environment and worker receipts. +- All 488 v1 tests passed, including 14 binding tests. Ruff across production, + evaluation support and tests plus strict MkDocs passed. +- Each worker checks saved-model replay before emitting artifacts. Positive means + and two-column fixed-scale AFT predictions passed; no test truth was scored. + +## Failed Attempts + +No failed fixture or real-data worker in this slice. No clipping, altered exclusions or new split rules +were introduced to make the worker inputs acceptable. + +## Risks and Follow-ups + +The numeric packets do not bind GLM/composed A9 controls yet. A4/A13, coupled +controls, full search/test release and restricted execution remain open. +Veteran's original-source license record remains unresolved. CPU smoke is not +survival quality, a CUDA check or full F0.3 acceptance. + +## Commits + +- This slice: `eval: bind counts paid amounts and survival worker inputs`. diff --git a/learnings/2026-09-06-v1-ranking-worker.md b/learnings/2026-09-06-v1-ranking-worker.md new file mode 100644 index 0000000..3bd622d --- /dev/null +++ b/learnings/2026-09-06-v1-ranking-worker.md @@ -0,0 +1,44 @@ +# 2026-09-06: Query-aware ranking comparator worker + +## Context + +The synthetic builtin capability probe did not provide an A4 worker with explicit +query identity, group weighting and validation/persistence semantics. + +## Decision or Result + +Require contiguous query IDs and disjoint training/validation queries. Translate +explicit query weights into each library's native representation; reject ordinary +row weights. Preserve query IDs outside features and score row identity unchanged. + +## Changes + +- [Ranking validation](../benchmarks/v1/ranking.py) and A4 branches in the + [baseline worker](../benchmarks/v1/baseline_worker.py). +- [Smoke](../benchmarks/v1/ranking_smoke.py), raw CPU histories and initial failure. +- Five focused group/weight/partition counterexamples. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_ranking_worker.py -n 0 -q`: five passed. +- `OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.ranking_smoke`: + XGBoost, LightGBM and CatBoost passed fit, named-NDCG stopping-minimum/maximum + checks and saved-score replay; see [evidence](../benchmarks/v1/evidence/ranking-cpu.json). +- Existing 30 fixed-round plus 30 stopping CPU cells still passed. +- Full suite after adapter additions: 460 passed, no skips. + +## Failed Attempts + +The initial test used the last native metric, assuming it was NDCG. CatBoost +also reports PairLogit, invalidating that assumption. The corrected test explicitly +selects the named NDCG series. Original source and error remain committed. + +## Risks and Follow-ups + +Native ranking optimizers, weighting and gain conventions differ; independent +v1 NDCG determines cross-method quality. Synthetic queries do not replace the +unavailable MSLR dataset/agreement. GPU and complete matrix integration remain open. + +## Commits + +- This slice: `eval: add query-aware ranking comparator workers`. diff --git a/learnings/2026-09-06-v1-real-data.md b/learnings/2026-09-06-v1-real-data.md new file mode 100644 index 0000000..f046c9a --- /dev/null +++ b/learnings/2026-09-06-v1-real-data.md @@ -0,0 +1,46 @@ +# 2026-09-06: Remaining real-data contracts and source gaps + +## Context + +The user asked to finish F0.3. A3/A6/A7–A10/A12/A13 still lacked parsed-data and +five-partition evidence. See [Sprint 016](../v1-sprints/016-f0-3-completion.md). + +## Decision or Result + +Freeze Covertype, Parkinsons, Concrete, Veteran, and the frequency/severity join. +Keep subject/recipe/policy partitions intact. Never convert a positive claim +count with missing payment into zero aggregate loss. MSLR and the original +Housing/Veteran license evidence remain unresolved, so this does not complete F0.3. + +## Changes + +- [Adapter](../benchmarks/v1/real_data.py), source catalog, and five data records. +- Nine independent checks cover partition isolation/completeness, field exclusions, + survival indicators, source corruption, and insurance join counterexamples. +- Separate CPU dependency inputs/hash lock for actual comparator installations; + these are evaluation dependencies, not production requirements. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_evaluation_data.py -n 0 -q`: 9 passed. +- Every real-data freeze replayed with `build/v1-env/bin/python -m benchmarks.v1.real_data NAME build/v1-data --verify benchmarks/v1/datasets/NAME.json`. +- Ruff on adapter/tests and strict MkDocs build passed. macOS/Python 3.12.12/NumPy 2.3.5. +- OpenML member MD5 matched official metadata; all source/member SHA256 values are frozen. + +## Failed Attempts + +- Initial import failed because the adapter did not exist. A one-row fixture then + exposed loadtxt's dimension collapse; preserve two-dimensional parsing explicitly. +- Microsoft OneDrive agreement retrieval failed (web redirect and API HTTP 400). + Original StatLib endpoints returned HTTP 403; do not infer licenses from availability. + +## Risks and Follow-ups + +- Ranking data, license gaps, trained encoders, survival IPCW support, complete + capability checks, runner integration, and independent held-outs remain. +- Frozen source/row identity is not predictive quality or proof of full F0 exit. +- Keep all raw outcome artifacts, including failures, separate from production claims. + +## Commits + +- This slice: `data: freeze remaining grouped and stratified evaluation inputs`. diff --git a/learnings/2026-09-06-v1-real-worker-binding.md b/learnings/2026-09-06-v1-real-worker-binding.md new file mode 100644 index 0000000..16a0f01 --- /dev/null +++ b/learnings/2026-09-06-v1-real-worker-binding.md @@ -0,0 +1,46 @@ +# 2026-09-06: Bind frozen real folds to comparator inputs + +## Context + +Synthetic comparator checks did not verify actual dataset-to-worker wiring. +A6 normalization and A12 structure inputs made implicit packet assembly unsafe. + +## Decision or Result + +Export Housing A1/A11, Parkinsons A6 and Concrete A12 ordinary-GBDT inputs only +after all five folds agree with the frozen source/encoder/row identities. Keep +worker validation arrays separate from test features and test truth. This is +an evaluation-side preparation operation, not an OS sandbox. + +## Changes + +- `benchmarks/v1/worker_data.py`: verified source binding, original-unit targets, + disjoint groups, A12 age feature and separate structural support. +- `benchmarks/v1/worker_data_smoke.py`: bounded fresh-process real validation fits. +- Seven focused counterexamples cover row order, changed encoder/target, duplicate + rows, crossed groups, output-unit preservation and structure alignment. + +## Verification + +- All 481 v1 tests passed, including seven new packet counterexamples. +- Ruff across production/evaluation/tests and strict MkDocs passed. +- Twenty real-data validation fits (four application paths, five folds) passed + in fresh CPU processes. XGBoost covers A1/A6/A12; CatBoost covers A11. + [Raw summary](../benchmarks/v1/evidence/real-worker-binding-cpu.json) records + exact commands, source/data/packet hashes, versions, stopping and artifact hashes. + Replay is checked inside each worker before success. No test truth was scored. + +## Failed Attempts + +Initial lint requested combining nested context managers in the smoke. No +mathematical behavior or frozen inputs changed to resolve it. + +## Risks and Follow-ups + +This exporter does not cover A2/A3/A4/A5/A7/A8/A9/A10/A13 or coupled controls yet. +Four-round validation plumbing cannot establish predictive quality, complete +16-trial selection, GPU execution or test-access isolation. Held-outs remain sealed. + +## Commits + +- This slice: `eval: bind frozen real folds to comparator worker packets`. diff --git a/learnings/2026-09-06-v1-reference-integration-exit.md b/learnings/2026-09-06-v1-reference-integration-exit.md new file mode 100644 index 0000000..8d58e98 --- /dev/null +++ b/learnings/2026-09-06-v1-reference-integration-exit.md @@ -0,0 +1,48 @@ +# 2026-09-06: Reference composition and F0.2 exit + +## Context + +F0.2 still needed finite offset, two-stage, quantile ensemble and accepted/best +state compositions before moving to the frozen evaluation protocol. + +## Decision or Result + +Close independent reference preparation after auditing all required application +families and development tasks. This does not close production or evaluation gates. +Best restoration needs a fresh version to reject abandoned-future proposals while +retaining the matched terms, coefficients, train/validation caches and logical step. + +## Changes + +- [Sprint 010](../v1-sprints/010-reference-integration-exit.md): bounded plan, + nine integration tests and phase reflection. +- [Acceptance ledger](../v1-sprints/f0-2-acceptance-ledger.md): required coverage + and remaining work assigned to F0.3/F1–F5 without dropping use cases. +- Immutable finite ensembles and Normal state probes; production import blocker + now exercises the positive ensemble composition too. + +## Verification + +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 288 passed, no skipped; macOS, Python 3.12.12, NumPy 2.3.5. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py`: pass. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict`: pass. +- Offset exactly once, paid-count join through model predictions, three quantiles, + two-round ordered commits, atomic rejection and base/nonzero best restoration. + +## Failed Attempts + +- Nonzero-best fixture initially compared unlike metric definitions; initialization + and all proposals must use the same validation score for meaningful selection. +- Review found broadcastable row/shape mismatches could evade numerical comparison; + explicit checks now reject them before commit, with a regression case. + +## Risks and Follow-ups + +- No public runtime, persistence, CUDA, external quality or author-cost validation. +- Next: F0.3 datasets/hashes, installed-library capability smoke, budgets, held-out + tasks and artifact judge. Do not turn synthetic reference success into parity claims. + +## Commits + +- This slice: `test: integrate reference models and close v1 F0.2` (parent `972f80a`). diff --git a/learnings/2026-09-06-v1-result-contract.md b/learnings/2026-09-06-v1-result-contract.md new file mode 100644 index 0000000..b61da15 --- /dev/null +++ b/learnings/2026-09-06-v1-result-contract.md @@ -0,0 +1,65 @@ +# 2026-09-06: Share result semantics without prescribing diagnostic record classes + +## Context + +Sprint 041 found run_many rejecting OrderedResult despite valid context/problem +state. Parent b99376d. The ordered package's nested parameter records should not +require an objective-specific scheduler branch or a built-in trace record. + +## Decision or Result + +Introduce structural RecipeResult and validate its accepted-state identity, +completed stop metadata and one trace entry per completed outer round. Preserve +the original result and recipe-owned diagnostic content. Ordered parameter commits +need not match outer-round counts. Remove the scheduler's dependency on recipes. +Malformed, unfinished or foreign results fail their individual run. + +This intentionally changes the foundation in response to exploratory evidence; +it cannot be counted as a frozen no-core-edit E5 attempt. The extension algorithm +source did not change. Earlier failed artifacts are preserved. + +## Changes + +- results.py: public protocol and validation; runs.py: use the shared contract. +- tests/v1/test_public_results.py: eleven cases covering the counterexample, + invalid results and mixed built-in/ordered M=1/8/32 scheduling. +- Installed verifier: ordered equality and mixed shared-preparation, stopping, + failures, permutation/regroup/retry and scoped-RNG checks. +- [Sprint 042](../v1-sprints/042-result-contract.md) and + [raw artifacts](../benchmarks/v1/evidence/recipe-results-042/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- Before editing, `uv run --no-sync pytest tests/v1/test_public_results.py::test_external_ordered_result_is_preserved -q -o addopts=''` + failed with the misleading foreign-state ValueError for a matching external result. +- `uv run --no-sync pytest tests/v1/test_public_results.py -q -o addopts=''`: + eleven passed after implementation. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 794 passed. +- `uv run --no-sync ruff check src/openboost examples/v1_extensions tests/v1/test_public_results.py`: + passed. `uv run --no-sync mkdocs build --strict` and `uv build --offline`: passed. +- `uv run --no-sync python examples/v1_extensions/verify.py /tmp/openboost-v1-results-042`: + isolated four-wheel installation, all development checks, and exact inference + of eight models after removal of all plugins passed. +- Verified recorded source/reference/artifact hashes against the final files. + macOS x86_64, Python 3.12.12, NumPy 2.3.5; one BLAS/OpenMP thread for installed checks. + +## Failed Attempts + +The first focused test reproduced the concrete-class rejection. No compatibility +shim or external result conversion was added; the shared contract addresses the +dependency directly. Historical Sprint 041 evidence retains the rejected result. + +## Risks and Follow-ups + +Runtime structural validation does not isolate arbitrary Python callbacks or copy +their diagnostic objects. Authors must honor input/state ownership. This is a +completed in-memory result contract, not a resume/serialization format. Next D1 +and remaining D5 author probes, with real worker integration; formal E2/E5/E6, +GPU cost and external adoption remain unverified. No push or publication. + +## Commits + +- This result-contract implementation; parent `b99376d`. diff --git a/learnings/2026-09-06-v1-scale-bound-selection.md b/learnings/2026-09-06-v1-scale-bound-selection.md new file mode 100644 index 0000000..c9d868d --- /dev/null +++ b/learnings/2026-09-06-v1-scale-bound-selection.md @@ -0,0 +1,56 @@ +# 2026-09-06: Verify A6 normalization before selecting current models + +## Context + +Parent e83e7b5. Sprint 017 identified arbitrary positive A6 selection weights as +unenforced despite correct worker scaling. Current A13 integration must not build +on that gap. + +## Decision or Result + +Bind hashed training targets and scale into A6 protocols. The audit checks exact +row alignment, recomputes population means/stds, and requires inverse-std weights. +Report mean standardized RMSE. Preserve the strict all-trials-success receipt rule +and trusted orchestrator digest requirements. Do not modify other applications. + +## Changes + +- selection.py: enforce scale/target/weight consistency and score definition. +- Eight selection tests, including the reproduced missing-binding acceptance and + a fixture where normalization changes the correct winner. +- current_selection_smoke.py: 16 current A6 configurations, retained outcomes, + independent audit, seal/re-audit/release and fresh-process selected inference. +- [Sprint 046](../v1-sprints/046-scale-bound-selection.md) and + [raw evidence](../benchmarks/v1/evidence/current-selection-046/README.md). + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- `uv run --no-sync pytest tests/v1/test_selection.py::test_a6_requires_scale_binding -q -o addopts=''`: + failed before implementation because no ValueError was raised. +- `uv run --no-sync pytest tests/ -m 'not gpu and not benchmark' -q --tb=short`: + 827 passed. Changed-file Ruff and strict MkDocs pass. +- `uv run --no-sync python -m benchmarks.v1.current_selection_smoke /tmp/openboost-selection-046`: + 16 successful trials; winner openboost:15; sealed selected prediction passes. +- Source hashes verified. macOS/Python 3.12.12/NumPy 2.3.5, one thread per trial, + 60-second fit and 30-second prediction caps; no RAM cap or CUDA. + +## Failed Attempts + +The counterexample accepted an unbound A6 protocol. The fix rejects it rather +than treating the protocol's arbitrary coefficients as evidence of train scaling. +The old denominator also did not report mean standardized RMSE, although fixed +coefficients gave the same within-fold ordering; the new score is explicit. + +## Risks and Follow-ups + +Trusted input provenance and pinned digests remain required. Packet separation +is not OS isolation. The synthetic grid is not the frozen real quality search; +all application adapters/real searches, A6 final quality aggregation, D5 and GPU +remain open. No test scores, speed or independent author/adoption claims. +Nothing pushed or published. + +## Commits + +- This scale-bound selection slice; parent e83e7b5. diff --git a/learnings/2026-09-06-v1-sequencing-audit.md b/learnings/2026-09-06-v1-sequencing-audit.md new file mode 100644 index 0000000..ee424da --- /dev/null +++ b/learnings/2026-09-06-v1-sequencing-audit.md @@ -0,0 +1,43 @@ +# 2026-09-06: Separate protocol readiness from evaluation completion + +## Context + +Repeated F0.3 slices added working real-data comparator bindings while the public +foundation remained unimplemented. The user requested an audit of prerequisites. + +## Decision or Result + +The current written F0.3 gate remains incomplete. Full E0–E7 results are later +phase exits, but adapters, matrices, judges and cohort/environment freezes are +explicit early requirements. An early B03 start is a proposed sequencing amendment, +not an interpretation that F0.3 has passed. Preserve all application scope. + +## Changes + +- [Sprint 017 audit](../v1-sprints/017-f0-sequencing-audit.md): evidence-backed + dependency ledger, implementation gaps and bounded B03–B06 overlap proposal. +- Sprint index/current execution record link the audit without changing phase gates. + +## Verification + +Inspected active phase/build/evaluation plans, actual integrity/quality/selection +paths, source freezes, safe held-out metadata, CPU/CUDA artifacts and the empty +production namespace. Strict MkDocs and whitespace checks passed for this slice. +No tests or benchmarks were rerun because this is a documentation audit. + +## Failed Attempts + +The earlier verbal recommendation understated explicit F0.3 requirements. The +audit corrects it: later result execution and earlier protocol implementation +are different requirements; a sequencing change must be recorded explicitly. + +## Risks and Follow-ups + +A6 standardized score/selection-scale binding, global coverage aggregation and +formal runtime access/resource enforcement remain substantive gaps. They cannot +be dismissed as documentation work. The proposed overlap does not authorize a +quality claim or CPU interface freeze, and is not adopted by this audit. + +## Commits + +- This slice: `docs: audit F0 prerequisites and proposed foundation sequencing`. diff --git a/learnings/2026-09-06-v1-shared-preparation.md b/learnings/2026-09-06-v1-shared-preparation.md new file mode 100644 index 0000000..697fc94 --- /dev/null +++ b/learnings/2026-09-06-v1-shared-preparation.md @@ -0,0 +1,58 @@ +# 2026-09-06: Explicit preparation reuse preserves run independence + +## Context + +Sprint 035 found that every recipe refitted binning despite shared data. +Parent 752a2ab; verification used this slice's dirty tree. + +## Decision or Result + +PreparedData fits training codes once and binds data/config identity. Every +built-in recipe accepts it explicitly; RunSpec forwards a dedicated field. +Original targets, weights and run state remain independent. M1/8/32 results +match independent preparation under reversed/regrouped execution. + +## Changes + +- binning.py: owned PreparedData and validated prepare_training resolver. +- recipes.py: twelve built-in preparation inputs; no other algorithm change. +- runs.py: explicit prepared field, reserved option name and failure isolation. +- Tests/docs: no-refit proof, identity/config mismatch and independent outcomes. + +## Verification + +Use UV_CACHE_DIR=/tmp/openboost-research-uv-cache. + +- tests/v1/test_public_preparation.py: five cases pass in final regression, + including M1/8/32; Binning.fit is disabled during all shared executions. +- uv run --no-sync pytest tests/ -m "not gpu and not benchmark" -q --tb=short: + 735 passed, macOS/Python 3.12.12/NumPy 2.3.5. +- uv run --no-sync ruff check src/openboost tests/v1/test_public_preparation.py: + passed after import ordering fixes. +- uv run --no-sync mkdocs build --strict; uv build --offline: passed. +- uv venv --python .venv/bin/python /tmp/openboost-preparation-wheel-001; + uv pip install --offline --python /tmp/openboost-preparation-wheel-001/bin/python + dist/openboost-1.0.0.dev0-py3-none-any.whl numpy==2.3.5: passed. +- Isolated Python -I from /tmp verified installed imports and executed every + docs/v1/*.md Python block in fresh namespaces: nineteen passed. + Build hash: [Sprint 037](../v1-sprints/037-shared-preparation.md). + +## Failed Attempts + +Initial PreparedData import failed before implementation. Lint corrected test +import ordering. A signature-only test was removed; runtime equivalence/no-refit +checks establish the meaningful behavior. + +## Risks and Follow-ups + +Training-code reuse is not inference caching or a measured speed result. +Different fixed budgets are not independent early stopping. Add validation +patience/stop state and M32 stop/failure isolation next. No GPU, fusion, real +selection or phase-exit claim. + +## Commits + +Committed with the shared-preparation slice; parent 752a2ab. + +Follow-up: removed a trailing blank line flagged by staged diff checking. +No runtime behavior changed; Ruff and diff whitespace checks pass. diff --git a/learnings/2026-09-06-v1-source-license-review.md b/learnings/2026-09-06-v1-source-license-review.md new file mode 100644 index 0000000..d835c28 --- /dev/null +++ b/learnings/2026-09-06-v1-source-license-review.md @@ -0,0 +1,41 @@ +# 2026-09-06: Housing hosted license evidence + +## Context + +The original data freeze recorded no archive license and inaccessible StatLib +source pages. Public availability alone did not establish a license. + +## Decision or Result + +Figshare article 3829992 version 2 declares CC BY 4.0 on file 5976036. +Its published MD5 matches the downloaded archive, whose SHA256 also matches +the original Housing freeze. Record this dated host declaration without rewriting +historical data/preprocessing evidence or claiming original-source rights review. + +## Changes + +- [Review](../benchmarks/v1/datasets/housing-license-review.json) retains public + API metadata, response digest, omitted account URL field names and file hashes. +- Evaluation README links the declaration and includes uploader attribution. + +## Verification + +Fetched public `https://api.figshare.com/v2/articles/3829992`. Independently +computed archive MD5 and SHA256 match the host and existing data freeze. +No model outputs were examined and no split or raw data was changed. + +## Failed Attempts + +Microsoft's linked MSLR agreement through the public OneDrive shares endpoint +returned HTTP 401. No authenticated access was bypassed. A4 source preparation +remains open; Veteran original-source license evidence also remains open. + +## Risks and Follow-ups + +The Figshare declaration is the uploader's hosted-file declaration. It is not +a separate verification of the original StatLib rights chain. Finish the remaining +source records before declaring the dataset gate complete. + +## Commits + +- This slice: `docs: record matching Housing host license evidence`. diff --git a/learnings/2026-09-06-v1-target-scaling.md b/learnings/2026-09-06-v1-target-scaling.md new file mode 100644 index 0000000..2d9ca31 --- /dev/null +++ b/learnings/2026-09-06-v1-target-scaling.md @@ -0,0 +1,46 @@ +# 2026-09-06: Preserve A6 target units through comparator training + +## Context + +The task card requires train-only target standardization and a saved inverse. +The numeric worker previously fit raw targets, so large-unit outputs dominated +shared multi-output fitting and native validation stopping. + +## Decision or Result + +Fit unweighted per-column training mean/std; constant columns use std one. +Transform validation with those same statistics and use zero standardized bases. +Fit and replay return original target units. Explicit training weights remain +training weights and do not redefine the preregistered normalization. + +## Changes + +- Worker saves target scale in the model bundle and training receipt. +- CPU smoke includes targets with very different units and a constant column, + verifies unchanged caller arrays and new-process A6 reload. +- Invalid scalar and empty-column A6 targets fail before library import. + +## Verification + +- `build/v1-env/bin/python` invoking `worker_smoke.run()` and `run(3)`: + 30 fixed and 30 stopping CPU cells passed. Raw metadata/results: + [worker-target-scale-cpu.json](../benchmarks/v1/evidence/worker-target-scale-cpu.json). + A6 fresh-process replay passed for all three comparators. +- `uv run --no-sync pytest tests/ -n 0 -q`: 474 passed. +- Ruff across production/evaluation/tests and strict MkDocs passed. + +## Failed Attempts + +The expanded fixture exposed CatBoost rejecting a constant target column. The +A6 adapter explicitly enables allow_const_label while retaining MultiRMSE and +zero initialization. This is needed for the task's constant-target contract. + +## Risks and Follow-ups + +CPU synthetic checks do not establish real-data quality or CUDA target-space +parity. Prior raw artifacts retain their original code identity; do not reinterpret +them as target-standardized runs. + +## Commits + +- This slice: `eval: preserve train-only multi-output target scaling`. diff --git a/learnings/2026-09-06-v1-validation-selection.md b/learnings/2026-09-06-v1-validation-selection.md new file mode 100644 index 0000000..738370b --- /dev/null +++ b/learnings/2026-09-06-v1-validation-selection.md @@ -0,0 +1,55 @@ +# 2026-09-06: Independent validation selection and sealed test release + +## Context + +F0.3 had numeric trial workers and metrics, but no connection from complete +validation search to a fixed winning model. A producer's claimed score, omitted +trial or replaced model must not determine the final test evaluation. + +## Decision or Result + +The trusted orchestrator pins the full selection protocol, then the independent +selector checks all 16 configurations per declared method and recomputes scores +from row-aligned validation arrays. It selects across methods and seals all scores +and artifact hashes. Release re-audits before opening test features. Hashes are +consistency checks whose custody matters; they are not proof of access chronology. + +## Changes + +- [Selection](../benchmarks/v1/selection.py): independent audit, exclusive receipt + creation, pinned receipt verification and test feature release. +- [Integration smoke](../benchmarks/v1/selection_smoke.py): actual isolated CPU + training jobs, audit/seal/release and selected-model inference in a new process. +- [Tests](../tests/v1/test_selection.py): missing/failed/duplicate trials, changed + config/protocol/model, fabricated scores/winner, partition overlap and test labels. + +## Verification + +- `uv run --no-sync pytest tests/v1/test_selection.py -n 0 -q`: 12 passed. +- `OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 build/v1-env/bin/python -m benchmarks.v1.selection_smoke build/v1-selection-smoke-001`: + 16 actual XGBoost worker processes completed, independent selection sealed and + selected-model inference completed in a new process. No real task was evaluated. + [Summary](../benchmarks/v1/evidence/selection-cpu.json) identifies code/environment; + the full generated packet remains in ignored build output and is reproducible. + +## Failed Attempts + +The initial focused test failed at import because the selection module did not +exist. API design explicitly excludes claimed scores rather than comparing them +with independently recomputed values that a caller might accidentally ignore. +A forged receipt hash alone cannot authorize a different winner: release re-audits. + +## Risks and Follow-ups + +The caller must pin protocol and receipt hashes independently of the producer. +Filesystem isolation, authenticated execution provenance, full method/task/device +matrix, early stopping and the full E3 judge remain unfinished. Row disjointness +does not replace query/entity/time split contracts. This is progress within F0.3, +not its exit. No OpenBoost production implementation or formal gate was added. + +## Commits + +- This slice: `eval: seal independent validation selection before test release`. + +Final verification: 443 tests passed with no skips; Ruff and strict MkDocs build +passed. The work remains on the existing design branch with no external publication. diff --git a/learnings/README.md b/learnings/README.md new file mode 100644 index 0000000..a390309 --- /dev/null +++ b/learnings/README.md @@ -0,0 +1,141 @@ +# OpenBoost Learnings + +This directory is the repository's durable engineering memory. It records why a +change was made, what evidence supports it, which attempts failed, and what +remains unknown. It is deliberately separate from release notes and generated +benchmark output. + +## When to Write an Entry + +Create or update an entry when work includes any of the following: + +- a non-obvious correctness fix; +- a benchmark, experiment, or falsified hypothesis; +- an architecture or product-scope decision; +- a dependency, platform, CI, packaging, or release incident; +- a user correction that should change future agent behavior. + +Use `YYYY-MM-DD-short-topic.md`. Prefer one entry per coherent investigation; +append follow-up evidence rather than creating many tiny diary files. + +## Required Content + +Start from `TEMPLATE.md` and include: + +- context and the question being answered; +- decision or result; +- files/behavior changed; +- verification and artifact locations; +- failed attempts and why they failed; +- remaining risks and next action. + +Keep entries factual and concise. Do not include credentials, secrets, private +URLs, or copied raw logs. Link the relevant commit after it exists. + +## Current Entries + +- [English repository prose](2026-09-06-english-repository.md) — repository-wide + translation with unchanged scope, gates, and historical evidence. + +- [Adult official test and stratified splits](2026-09-06-v1-adult-data.md) — A2 raw + data freeze, with missing categories retained and fnlwgt excluded. + +- [Housing five splits](2026-09-06-v1-housing-data.md) — A1/A11 share verified + legacy inputs plus seeds 3–4; licensing and quality remain unresolved. + +- [A5 Bike data freeze](2026-09-06-v1-bike-data.md) — verified archive, calendar-only + inputs and five complete-date rolling windows; no model quality result yet. + +- [v1 artifact integrity](2026-09-06-v1-artifact-integrity.md) — missing cells, + stale cache identities and false pass claims fail; quality gates remain unevaluated. + +- [Reference integration and F0.2 exit](2026-09-06-v1-reference-integration-exit.md) — + finite model/state compositions, 288 tests; next F0.3, production gates pending. + +- [Retire legacy production](2026-09-05-retire-legacy-production.md) — user-directed + clean v1 package reset; historical code at `50acfc6`, current training API pending. + +- [v1 sprint execution](2026-09-05-v1-sprint-execution.md) — sprint plans/results + and reflection now live in `v1-sprints/`; starts B01/F0.2 scalar/tree references. + +- [Foundation construction design](2026-09-05-foundation-construction-design.md) — + concrete module/data/operation/state/device contracts and B01–B14 build slices; + distinguishes the engineering design from task cards and independent oracles. + +- [F0.1 foundation task cards](2026-09-05-foundation-f0-task-cards.md) — + all A1–A13 tasks specified, independent failure cases and fair baseline paths; + F0.2 is next, runtime and benchmark gates remain unverified. + +- [OpenBoost v1 scope, releases and evaluation](2026-09-05-openboost-v1-plan.md) — + real v1 planning baseline; all A1–A13 use cases individually required, no + privileged insurance/AFT focus; releases/plans and quantitative acceptance. + +- [Foundation product and clean redesign](2026-09-05-agent-foundation-reset.md) — + latest user direction, supersedes the earlier risk-first investment ordering; + active F0–F5 plan, no backward compatibility requirement. + +- [Independent GPU extension wheels](2026-09-05-foundation-p6-gpu-wheels.md) + +- [Strict CUDA extension trainer](2026-09-05-foundation-p5-trainer.md) + +- [Independent CPU extension wheels](2026-09-05-foundation-p6-cpu-wheels.md) + +- [P4.4 level-wise builder and T4 evidence](2026-09-05-foundation-p4-builder.md) + +- [2026-09-05-foundation-p4-leaves.md](2026-09-05-foundation-p4-leaves.md) — real-row CPU/CUDA leaf reduction, explicit leaf rule, bounded next-gradient checks and real T4 evidence. + +- [2026-09-05-foundation-p4-splits-goal-review.md](2026-09-05-foundation-p4-splits-goal-review.md) — goal/value checkpoint, earlier independent CPU package validation, numeric split/routing and real T4 evidence. + +- [2026-09-05-foundation-p4-histograms.md](2026-09-05-foundation-p4-histograms.md) — fixed-slot CPU/CUDA histogram contract, independent sample oracle and real T4 validation. + +- [2026-09-05-foundation-p3-cpu-contract.md](2026-09-05-foundation-p3-cpu-contract.md) — experimental CPU objective/builder/schedule, plugin-free wheel inference, coefficient persistence and installation limits. +- [2026-09-05-foundation-p2-boundaries-baseline.md](2026-09-05-foundation-p2-boundaries-baseline.md) — real T4 boundary verification and frozen California Housing baseline; P2 complete. +- [2026-09-05-foundation-p2-correctness.md](2026-09-05-foundation-p2-correctness.md) — real T4 weighted regression before/after evidence, scoped RNG and explicit execution boundaries. +- [2026-09-05-foundation-p1-modal.md](2026-09-05-foundation-p1-modal.md) — isolated Modal wheel smoke, provenance checks, and explicit failure propagation. +- [2026-09-05-foundation-p0-integration.md](2026-09-05-foundation-p0-integration.md) — integrate unified trainer, preserve local fixes, and cover generic loading of the new models. +- [2026-09-05-gpu-python-foundation-design.md](2026-09-05-gpu-python-foundation-design.md) — scoped GPU Python foundation design, branch integration, experimental contracts, and medium execution checklist. +- [2026-09-05-impact-adoption-value-strategy.md](2026-09-05-impact-adoption-value-strategy.md) — impact/adoption/value research, current online versus local evidence, and proposed validation gates. +- `2026-08-15-repository-audit.md` — product focus, correctness risks, and + evidence gaps found in the deep audit. +- `2026-08-15-scoringbench-integration.md` — third-party benchmark integration, + validation, and Intel macOS runtime limitation. + +- [2026-09-05: v1 data and classification references](2026-09-05-v1-data-classification-reference.md) — independent column transforms, geometry and two-round checks. + +- [2026-09-05: ranking, quantile and vector references](2026-09-05-v1-ranking-quantile-vector-reference.md) — separate pair geometry, split statistics and leaf solvers. + +- [2026-09-05: positive targets and AFT](2026-09-05-v1-positive-aft-reference.md) — exposure, paid-loss definitions and stable censored geometry. + +- [2026-09-05: Normal and Formula references](2026-09-05-v1-normal-formula-reference.md) — direction fitting and immutable candidate updates. + +- [2026-09-05: identity and isolated runs](2026-09-05-v1-identity-runs-reference.md) — prepared row binding, stable RNG and comparable selection. + +- [2026-09-05: exact author mutation references](2026-09-05-v1-author-mutation-reference.md) — expectile, penalized quantile and ordered acceptance. + +- [2026-09-05: mixed/vector growth references](2026-09-05-v1-mixed-vector-growth-reference.md) — fitted transforms, multilevel routing and full vector leaves. + +- [CPU/CUDA comparator preflight](2026-09-06-v1-comparator-capabilities.md): installed support, native failures and isolated T4 evidence. +- [Validation-only baseline worker](2026-09-06-v1-baseline-worker.md): explicit inputs, saved offsets and the remaining search integration boundary. +- [Validation selection and test release](2026-09-06-v1-validation-selection.md): independent scores, complete trials, pinned receipts and access-sequence limitations. +- [Native baseline early stopping](2026-09-06-v1-early-stopping.md): weighted validation, selected iteration persistence and new-process counterexamples. +- [Query-aware ranking worker](2026-09-06-v1-ranking-worker.md): explicit group weights, partition checks and named metric selection. +- [Parametric and paid-loss workers](2026-09-06-v1-parametric-workers.md): explicit target units, positive-payment reconstruction and global-formula controls. +- [Auxiliary quality metrics](2026-09-06-v1-auxiliary-metrics.md): calibration/class diagnostics, supported IPCW scoring and descriptive paired intervals. + +- [A6 target scaling](2026-09-06-v1-target-scaling.md): train-only normalization and saved original-unit predictions. + +- [Housing hosted license](2026-09-06-v1-source-license-review.md): matched archive and dated attribution evidence. + +- [Held-out seal](2026-09-06-v1-heldout-seal.md): separate evaluator context, opaque archive custody and pending execution gates. + +- [Real worker binding](2026-09-06-v1-real-worker-binding.md): frozen folds, target units and separate evaluation packets. + +- [Classification and quantile binding](2026-09-06-v1-classification-quantile-binding.md): source IDs, train-only categories and chronological prefixes. + +- [Counts and survival binding](2026-09-06-v1-positive-survival-binding.md): policy populations, exposure contracts and frozen censoring support. + +- [F0 sequencing audit](2026-09-06-v1-sequencing-audit.md): literal prerequisites, later evaluation gates and an explicit overlap proposal. + +- [B03 public CPU components](2026-09-06-v1-b03-cpu-state.md): immutable inputs, keyed runs, atomic state and constant-term inference. + +- [B04 numeric operations](2026-09-06-v1-b04-numeric-ops.md): quantile preparation, weight ownership, histogram/reference parity and replaceable constraints. diff --git a/learnings/TEMPLATE.md b/learnings/TEMPLATE.md new file mode 100644 index 0000000..a8efe46 --- /dev/null +++ b/learnings/TEMPLATE.md @@ -0,0 +1,32 @@ +# YYYY-MM-DD: Topic + +## Context + +What question, bug, or decision triggered this work? + +## Decision or Result + +What did we decide or learn? Separate measured facts from hypotheses. + +## Changes + +- File or subsystem: behavioral change and reason. + +## Verification + +- Exact focused test or benchmark. +- Result and artifact location. +- Environment limitations that affect interpretation. + +## Failed Attempts + +- Attempt: failure mode and the rule learned from it. + +## Risks and Follow-ups + +- What remains unverified? +- What is the next concrete gate? + +## Commits + +- `SHA` — subject diff --git a/mkdocs.yml b/mkdocs.yml index 08c45df..8c6140d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,123 +1,36 @@ -site_name: OpenBoost -site_description: GPU gradient boosting for distributional regression. Every parameter of F(y|x) gets its own tree ensemble, trained with the full natural gradient. +site_name: OpenBoost v1 +site_description: Programmable boosting foundation for researchers and AI agents, under construction. site_url: https://jxucoder.github.io/openboost repo_url: https://github.com/jxucoder/openboost repo_name: jxucoder/openboost - +docs_dir: docs/v1 theme: name: material - palette: - - scheme: default - primary: deep purple - accent: amber - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - scheme: slate - primary: deep purple - accent: amber - toggle: - icon: material/brightness-4 - name: Switch to light mode - features: - - navigation.instant - - navigation.tracking - - navigation.tabs - - navigation.sections - - navigation.top - - search.suggest - - search.highlight - - content.code.copy - - content.code.annotate - - toc.follow - icon: - repo: fontawesome/brands/github - plugins: - search - - mkdocstrings: - handlers: - python: - paths: [src] - options: - docstring_style: google - docstring_section_style: spacy - show_source: false - show_root_heading: true - show_root_full_path: false - show_symbol_type_heading: true - show_symbol_type_toc: true - members_order: source - separate_signature: true - signature_crossrefs: true - merge_init_into_class: true - show_docstring_examples: true - markdown_extensions: - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - - pymdownx.details - - admonition + - fenced_code - tables - - attr_list - - md_in_html - - toc: - permalink: true - -extra: - social: - - icon: fontawesome/brands/github - link: https://github.com/jxucoder/openboost - - icon: fontawesome/brands/python - link: https://pypi.org/project/openboost/ - nav: - - Home: index.md - - Getting Started: - - Installation: getting-started/installation.md - - Quickstart: getting-started/quickstart.md - - GPU Setup: getting-started/gpu-setup.md - - User Guide: - - Distributional & varying-coefficient: - - How it works: user-guide/how-it-works.md - - NaturalBoost: user-guide/naturalboost/overview.md - - Distributions: user-guide/naturalboost/distributions.md - - Custom Distributions: user-guide/naturalboost/custom-distributions.md - - FormulaBoost: user-guide/formulaboost.md - - Weibull AFT: user-guide/survival.md - - Mean regression: - - Gradient Boosting: user-guide/models/gradient-boosting.md - - Multi-class Classification: user-guide/models/multiclass.md - - DART: user-guide/models/dart.md - - OpenBoostGAM: user-guide/models/gam.md - - Linear Leaf GBDT: user-guide/models/linear-leaf.md - - Training: - - Callbacks: user-guide/training/callbacks.md - - Large-Scale Training: user-guide/training/large-scale.md - - Evaluation: - - Metrics: user-guide/evaluation/metrics.md - - sklearn Integration: user-guide/sklearn-integration.md - - Model Persistence: user-guide/model-persistence.md - - Tutorials: - - Uncertainty Quantification: tutorials/uncertainty.md - - Custom Loss Functions: tutorials/custom-loss.md - - Cookbook: - - Extending OpenBoost: cookbook/index.md - - Custom Loss: cookbook/custom-loss.md - - Custom Distribution: cookbook/custom-distribution.md - - Custom Growth Strategy: cookbook/custom-growth-strategy.md - - Device-Native Loss (GPU): cookbook/device-loss.md - - Benchmarks: benchmarks.md - - API Reference: - - openboost: api/openboost.md - - Models: api/models.md - - Distributions: api/distributions.md - - Callbacks: api/callbacks.md - - Loss Functions: api/loss.md - - Utilities: api/utils.md - - Migration: - - From XGBoost: migration/from-xgboost.md - - Changelog: changelog.md + - Current v1 status: index.md + - CPU state components: cpu-state.md + - Numeric operations: numeric-ops.md + - Tree policies: trees.md + - Squared boosting: squared.md + - Normal boosting: normal.md + - Formula and sequential runs: formula-runs.md + - Categorical features: categorical.md + - Binary classification: binary.md + - Multiclass and vectors: multiclass.md + - Query-local ranking: ranking.md + - Quantile and penalized leaves: quantile.md + - Poisson counts and exposure: poisson.md + - Gamma positive-target means: gamma.md + - Tweedie nonnegative means: tweedie.md + - Frequency and severity: frequency-severity.md + - Censored log-normal AFT: aft.md + - Multi-output squared regression: multioutput.md + - Shared training preparation: preparation.md + - Independent stopping: stopping.md + - Public development extensions: extensions.md + - Shared recipe results: results.md diff --git a/planning/agent-boosting-foundation-plan.md b/planning/agent-boosting-foundation-plan.md new file mode 100644 index 0000000..7eea488 --- /dev/null +++ b/planning/agent-boosting-foundation-plan.md @@ -0,0 +1,429 @@ +# OpenBoost v1: Programmable boosting foundation design, execution and acceptance + +Date: 2026-09-05. Version: **the real v1 planning baseline**, explicitly designated by the user. +Status as of Sprint 038: **F0.1/F0.2 delivered; F0.3 open; broad CPU construction delivered, formal F1 exit and author/quality/cost evaluation incomplete**. +v1 denotes this product/architecture goal, not the existing PyPI version or a claim that P0–P7 completed v1. +Code-review baseline: 3ac1552; branch: codex/gpu-python-foundation-design. +Execution: [v1-sprints](../v1-sprints/README.md); Sprint 010 completed the +[F0.2 exit audit](../v1-sprints/f0-2-acceptance-ledger.md). Each sprint records plan/results/reflection; +phase status follows evidence. At the user's request, Sprint 002 retired old production early. +Reproduce it at `50acfc6`. The package now has shared CPU components, twelve +recipes and explicit training-preparation reuse. See the +[current goal/progress review and execution plan](../v1-sprints/038-goal-progress-and-plan.md) +for delivered boundaries and remaining work. Amended phase dependencies and +acceptance gates below still apply; implemented recipe count is not a phase exit. + +This plan supersedes future investment order in the old GPU checklist and the +[ScoringBench-first plan](impact-adoption-value-next.md). Old experiments and failures remain valid. +The user states that the foundation is the product and use cases determine abstractions. +**No backward compatibility is required: APIs, trainers, representations and persistence may be rewritten.** +Planning/F0.1 did not start a rewrite or GPU job; subsequent execution follows these dependencies. + +The v1 specification consists of: + +- This file: product goals, scope, architecture, dependencies, phase deliverables and completion. +- [Application contracts](foundation-application-contracts.md): all required A1–A13 and data choices. +- [F0.1 task cards](foundation-tasks.md): inputs/outputs, data, mathematics, oracles, baselines, interface sketches. +- [Construction design](foundation-construction-design.md): modules, records, function contracts, + tree/state/device execution and B01–B14 implementation slices. +- [Acceptance/evaluation](openboost-v1-evaluation.md): E0–E7, quantitative gates, comparisons, failures and artifacts. +- [Release/plan review](boosting-release-review-2026-09-05.md): sourced competitive facts and shipped/experimental/planned status. + +**Every listed case requires individual delivery and acceptance.** Classification, regression, ranking, +quantiles, multioutput, counts, positive amounts, total loss, survival, distributional prediction, Formula +and train-many jointly constrain the foundation. Insurance/AFT have no special priority. A few +important cases or a task count cannot replace A1–A13. Dependencies determine implementation order, +not whether a case belongs in v1 completion. + +## 1. Revisit the product hypothesis + +Help researchers and agents turn a boosting idea into a verified, reusable implementation at lower +cost. Optimize **total cost from algorithm change to trustworthy results**: implementation, debugging, +verification, repeated training and deployment, not just one fit, Python percentage or public-function count. + +Validate four separate hypotheses rather than merging them into one technical test: + +1. Useful problems require internal algorithm changes that existing configuration/hooks cannot satisfy cheaply. +2. Composable components substantially reduce that work across structurally different algorithms. +3. Agents can use them correctly and receive enough diagnostics to locate failures. +4. End-to-end workflow benefits outweigh runtime overhead, learning cost and a new dependency. + +Initial users are researchers, domain modelers and their agents with actual modification needs. +One-call tabular recipes help trials; independently publishable author recipes/packages are the +adoption path. Users enter through a method, then reuse components and publish their own. +Impact means reuse in independent research/decisions; adoption means independent completion/repeated use; +value means total verified-change cost and task results. Do not yet infer willingness to pay or a business model. + +### Correct competitive assumptions + +- XGBoost has Python custom objectives/metrics, grow policies, updaters and leaf constraints. + Custom loss, depthwise/lossguide switching or simple leaf clipping is not automatically distinctive. + [Custom objectives](https://xgboost.readthedocs.io/en/stable/tutorials/custom_metric_obj.html), + [parameters](https://xgboost.readthedocs.io/en/stable/parameter.html). +- LightGBM exposes update(fobj=...), rollback and set_leaf_output; existing libraries are not entirely + closed to training/leaf changes. [Booster](https://lightgbm.readthedocs.io/en/stable/pythonapi/lightgbm.Booster.html). +- NGBoost supports new distributions, scores and metrics; Laplace is an easy control task. + [Development guide](https://stanfordmlgroup.github.io/ngboost/5-dev.html). +- Py-Boost provides Python GPU boosting/customization and is a direct foundation comparator, + not just XGBoost/LightGBM. [Repository](https://github.com/sb-ai-lab/Py-Boost). +- Review-date stable versions: XGBoost 3.4.1, CatBoost 1.2.10, LightGBM 4.7.0. XGBoost 3.4 expands hist + vector leaves; CatBoost has GPU custom objectives; LightGBM 4.7 adds GPU/data interoperability. + See the release review for limits/plans. Do not assume competitors lack multioutput or custom GPU losses. + +These are review-date documentation findings; pin actual versions and verify task paths when executing. +The opportunity is modification cost/verifiability, not denying competitor extensibility. Controls +may use hooks, outer loops and source edits; never artificially restrict competitors. + +“At least as good as XGBoost” has separate meanings: expressing a standard algorithm, matching task +quality, and speed/engineering completeness. Arbitrary agent changes cannot be guaranteed to improve +results. Supply reproducible baselines, select with validation and evaluate independent test. +Baseline selection protects workflow value, but calling XGBoost is not rebuilding it with our components. + +## 2. Extract variation axes from use cases + +These are foundation test programs, not four simultaneous complete product lines. + +| Use case | Required algorithm decisions | Minimal testable implementation | +|---|---|---| +| XGBoost/LightGBM-style GBDT | Statistics, split scoring, leaf solving, depthwise/best-first, sampling | Scalar second-order boosting on fixed bins; two growth orders sharing statistics/routing | +| NaturalBoost/NGBoost-style | Links, proper score, Fisher/curvature, direction, learner fit, step acceptance | Two-parameter Normal; fixed steps/bounded training-loss backtracking; rejection cannot pollute model | +| FormulaBoost | Separate theta(Z) from formula(theta, x), parameter coupling, structural inputs/outputs | Two-parameter formula, independent Jacobian/GGN, at least two rounds composed from trees | +| Train-many | Data reuse, independent state/RNG/stopping, execution groups | Multiple recipes/configurations on shared data; sequential reference invariant to run order | + +At the reviewed historical baseline, FormulaObjective already solves coupled GGN directions then +fits per-parameter trees. This differs from shared topology/joint leaf solving; do not claim the +former was inexpressible. fit_trees_batch already had a sequential shared-binned-input path; +retain its semantics, not count renaming/wrapping as train-many progress. + +Keep two tasks out of interface design to detect overfitting to our examples. Synthetic data may +check mathematics initially; adoption/value require real scenarios. + +**Applications** form another axis: classification, regression, ranking, quantiles, counts/positive +values, censoring, multioutput, structure and model selection. Insurance/AFT probe offsets/units and +censoring; ranking probes within-group dependencies; classification probes links/mappings; multioutput +probes shared trees/parameter axes. Structure×application is a design matrix, not a mandatory first-round +Cartesian product. Every application still needs implementation, independent semantics and real evaluation. +Dataset choice cannot make applications optional. A1–A13 plus R1–R9/C1–C7 define v1 scope. + +### Minimum v1 deliverables + +All capabilities below are required at v1 completion. F1 can begin with a subset but cannot call +it complete v1. CPU is both independent reference and execution entry for all recipes; GPU has explicit subsets. + +| ID | Required recipe/task | Minimum behavior/check | v1 CUDA scope | +|---|---|---|---| +| R1 | Regression, binary, multiclass | Squared/logistic/softmax, mappings, weights, probabilities, missingness | Numeric dense including missing required; native categories later | +| R2 | Quantile/robust | Weighted quantile accessing routed residuals/weights, not just G/H | CPU required; CUDA explicitly optional | +| R3 | Group ranking | Pairwise logistic and NDCG-weighted lambda, groups/pairs/group metrics | CPU required; CUDA optional | +| R4 | Count/positive/aggregate | Poisson offset, Gamma/Tweedie fixed-parameter means; targets separate from weights/exposure | Poisson offset required; Gamma/Tweedie optional | +| R5 | Censored AFT | Fixed noise family/scale, events/right censoring, canonical intervals; reject others | Numeric events/right censoring required | +| R6 | Natural/distributional | Two-parameter Normal, Fisher/ordinary, fixed/backtracking, proper scores | Numeric Normal required | +| R7 | FormulaBoost | Two-parameter formula, links, structural inputs, independent Jacobian/directions, two rounds | CPU required; mixed execution declared separately | +| R8 | Multioutput/vector leaves | Independent trees and shared vector topology; split/leaf statistics may differ | Numeric squared-error/diagonal statistics required | +| R9 | Train-many | M=1/8/32, shared prepared data, independent config/seed/stop/failure, sequential reference | At least one batched compatible R1/R4/R8 group required | + +| ID | Cross-recipe capability | Completion standard | +|---|---|---| +| C1 | Typed data/targets | Numeric/missing, CPU categories, mapping/unseen, weights, offset, group, interval, structure; train-only preprocessing | +| C2 | Composable trees | Additive stats+one extra channel, replaceable feasibility/score, three CPU policies, actual routed rows | +| C3 | Learner/leaf replacement | Scalar/vector payload, Newton/weighted quantile, output schema separate from parameter axis, custom split constraint | +| C4 | Explicit runtime | Scoped device/workspace/RNG, candidate/accept/reject/stop, independent runs, visible sync/transfer/fallback, no global backend | +| C5 | Artifacts | Raw/user spaces, mappings, base/link/offset, trees/coefficients/vector leaves, new persistence, readable diagnostics | +| C6 | Evaluation/authoring | Independent oracles, five change types, two unseen tasks, real tasks, all failures, external wheel packages | +| C7 | Usable workflow | Install→baseline→read/change recipe→verify→save/load→CPU inference; capabilities/errors/reproducibility | + +Native categorical v1 may select one verifiable split method; full CatBoost ordered boosting/CTR +replication is not required. CSR/CSC, text/embeddings, full Cox/competing risks/truncated survival, +all constraints, linear leaves, DART/GOSS, distribution catalogs, autodiff compilers, Ray, multi-GPU and +out-of-core belong to a justified later list. Never silently accept their parameters. F0 records +competitor status and why v1 excludes them while preserving extensible boundaries. + +## 3. Architecture: Programmable algorithms, explicit state and bulk operations + +This is the overview; module/function decisions live in [construction design](foundation-construction-design.md). +Planning includes how to build the foundation; tasks/evaluation constrain and judge it. F0.2 is +independent mathematical preparation; public components, recipes and runtime are built in F1. + +The old Booster(objective, builder, schedule) shell need not survive. **Recipes are ordinary Python +algorithm programs and may own the loop. Runtime manages resources/state without imposing one +training order on every algorithm.** Convenience models are thin recipe entry points. + +| Boundary | Responsibilities | Do not hardcode | +|---|---|---| +| Problem/parameter state | Data roles, targets, weights, structure, raw parameters, links, initialization/evaluation | One y for every task; all auxiliary data are weights | +| Recipe | Scores/directions, growth, learner choice, parameter order, acceptance/stopping | One tree per parameter each round; only predetermined learning-rate schedules | +| Components/bulk ops | Binning, routed aggregation, candidate stats/scoring, partition, leaf solves, predict/update | Only G/H, fixed511-slot trees, loss equals curvature | +| Runtime/run state | Device/stream/workspace, buffer ownership, RNG, commit, batch execution | Global backend, parameter axis equals model axis, automatic arbitrary-Python compilation | +| Artifacts/verification | Serializable model state, declared inference, diagnostics, independent references | Arbitrary closure serialization; verification proves arbitrary algorithm correctness | + +Prefer a few arrays, structured records and ordinary functions. Initial choices may change with +actual F1 usage; freeze public protocols only after multiple consumers validate them. + +### How much of the algorithm is programmable + +Design pseudocode, not an existing or finalized API: + +```python +with runtime.run(data, seed=seed) as run: + state = initialize(problem, run) + for step in range(budget): + geometry = problem.geometry(state) # explicit loss/derivative/curvature semantics + targets = direction_rule(geometry, state) # may couple parameters + learner = grow(data, targets, split_rule, leaf_rule, growth_policy, run) + candidate = propose(state, learner) # accepted state is unchanged + decision = accept(candidate, problem, run) # fixed step or backtracking + state = run.commit(state, candidate, decision) + artifact = export_model(state) +``` + +grow must itself be readable composition: aggregate→candidate statistics→score/feasibility→ +choose→partition→solve leaves. Authors can replace a piece or call operations to write another +grow. Do not expose grow only to hide decisions in a second black box. A callback/registry or +universal scheduling graph for every small function is unnecessary. + +### Required semantics + +- **Statistics versus directions:** name exact Hessians, PSD approximations, Fisher, pseudo-responses + and fit weights separately. Weight application has one visible owner; do not disguise all directions as G/H. +- **Proposed versus accepted state:** joint/ordered behavior is explicit; backtracking has bounded + trials. Rejection leaves no trees, coefficients, raw or best-state residue. RNG follows run/step/component, + not scheduler order. Full data/model copying per proposal is not required. +- **Extensible shared statistics:** start with G/H/count plus one extra additive channel; declare + shape, dtype, reduction, memory budget/supported scores. No promise to accelerate arbitrary Python reducers. +- **Leaves beyond additive statistics:** weighted quantiles need routed rows/residuals via explicit + views or declared statistics; do not force them into Newton form. +- **Cross-row objectives:** ranking pairs/queries and Cox risk sets cannot be excluded by a row-independent + protocol. Direction computation differs from tree per-row reduction. +- **Model versus parameter axes:** one run has K parameters; M runs may have different K, tree counts, + configurations and stopping. Begin with independent state lists, packing compatible groups only; + do not require giant M×N×K tensors. +- **Learners versus parameter updates:** learners declare output schemas; recipes map them to parameters. + Runtime does not equate one tree with one channel. Separate topology/payload. Scalar first is allowed, + but R8 must actually verify vector leaves in v1, not merely leave conceptual space. +- **Identity for reuse:** sharing requires matching training rows, bin strategy, binning weights and + metadata. Do not bin full data across validation folds. Align indices, structure and weights. +- **Explicit inference capability:** core readers predict standard trees/raw parameters; custom + formulas/links declare dependencies or supported representations. Do not promise inference after + arbitrary formula code is uninstalled. New formats may reject old ones, but their own round trips must work. + +### Python and GPU tradeoffs + +Python owns editable decisions; large arrays stay on device. GPU remains a design goal; CPU is an +installable entry/reference and device execution is considered early. Start with NumPy references, +CuPy arrays and a few existing CUDA kernels; change kernel DSL only when profiling justifies it. +Numba/CuPy/Python percentage are not product promises. Do not first build a compiler or compare every GPU framework. + +Verified common combinations may use batched/fused implementations with identical semantics to +readable composition. Changing recipes requires capability rechecking, not silently ignoring edits. +Arbitrary Python loops may synchronize or not accelerate; reports must say so. GPU baseline fits +retain device trees/predictions and export explicitly rather than CPU-snapshotting every tree for prediction. + +Separate development oracles, runtime boundary checks and export validation. Redesign ownership/check +granularity instead of adding a trust-plugin switch to an inefficient path. Cheap checks do not +replace mathematics; read-only arrays/verifiers are not security sandboxes for arbitrary Python. + +## 4. Existing assets and constraints + +| Asset/constraint | Treatment | +|---|---| +| Independent CPU math/routing, real CUDA conformance, wheel installs, failure reproductions | Preserve semantics/raw evidence; rewrite tests for new APIs without deleting correctness conditions | +| Channel-based _trainer.py and round/channel/lr schedule | Replace entirely if useful; do not inherit its loop restrictions | +| Exact TreeStructure, depth0–8, fixed slots, host snapshots | Old boundaries, not requirements; new layouts still declare resource/support limits | +| FormulaObjective, distribution mathematics, sequential fit_trees_batch | Reuse verified mathematics or rewrite, never as the sole oracle | +| Old APIs/loaders/experimental plugins | No compatibility, dual-write, deprecation cycle or shim required; historical revisions reproduce old behavior | +| ScoringBench parameter fix `3ac1552` | Preserve useful fix for distributional quality, not whole-foundation scheduling | + +[P7 T4 evidence](../benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md) +shows experimental warm fit **12.888×** same-run legacy CUDA, failing the original 1.2 gate. +This shows real overhead, not that programmable foundations lack value. Do not rewrite the old +threshold; freeze new algorithms/workloads separately before measurement. The independent extension +also worsened a proper score; retain that counterexample. + +## 5. Execution order and acceptance + +Use F0–F5, not old completed P0–P7 labels. Each subtask is a reviewable commit. F0.1/F0.2 are delivered; +continue F0.3 through dependencies/evidence. Construction-design section9 maps phases to B01–B14. + +### F0: Specify required algorithms and judging first + +- [x] **F0.1: Task cards and alternatives audit.** [Cards](foundation-tasks.md) fix all recipes' + inputs/outputs, two-round state changes, boundaries/oracles from four structural probes. Audit + incumbent hooks and source-edit paths/costs, especially Py-Boost. Complete R1–R9/C1–C7/A1–A13 + with shipped/planned status, data/targets/outputs, oracles, processing, metrics, phases/evidence. + Missing data remains pending, never removed or covered by another task. Cards/interface sketches + map requirements to E0–E6 instead of another vague roadmap. Specification coverage passes; + actual bytes/hashes, capability smoke, budgets/held-out tasks belong to F0.3. Engineering design + supplies records, ops, three growth policies, transactions, inference, CPU/CUDA/train-many construction; + specifications are not existing public components. +- [x] **F0.2: Original NumPy references/counterexamples.** tests/v1/reference independently + enumerates splits, reduces rows and solves small matrices without production imports. Hand + cases cover duplicate-threshold ties, empty/zero-weight children, invalid candidates, rejection, + parameter order and exchanged run order. External GBDT comparisons distinguish binning/ties; + different algorithms need not have bitwise-identical full trees. Cover class/link, query/pair, + weighted quantiles, vectors, offset/weight/units, event density versus censoring probability, + distribution geometry, formula Jacobians and isolation. Valid infinite bounds must not be rejected as generic non-finite labels. +- [ ] **F0.3: Freeze/implement comparison protocol.** benchmarks/v1 manifest/runner/judge pin + library/agent versions, tasks, public/held-out examples, budgets, tolerances, quality thresholds, + CPU/GPU environments and expected matrix. Run old/reference baselines first; declare cost budgets + before new code. Implement [E0–E7](openboost-v1-evaluation.md), testing missing/polluted/failed artifacts. + Pin recent three-library releases; CPU smoke before GPU-variant preflight. Do not inherit stale + completion labels. No full evaluation before actual budgets/data hashes are frozen. + +Initial development probes: an incumbent-friendly new-loss/distribution control; candidate splits +requiring minimum per-cohort information mass, with independent information_weight separate from +training weights; and bounded multiparameter backtracking/rejection. The cohort task is a statistical +stability experiment, not claimed innovation or proven demand. If incumbents solve it easily, record +that rather than increasing difficulty. Leaf solving/run scheduling complete the five E2/E5 change types. + +F0 exit: all A1–A13 cards, falsifiable judgments, fair comparators and explicit changes. Missing data +or judges prevent exit; task totals cannot substitute. Complete F0.1–F0.3 before F1 except for the approved B03–B06 overlap below; separate design/ +reference commits must not smuggle in model migration or kernel optimization. + +### Approved sequencing amendment: 2026-09-06 + +The user approved the [Sprint 017 audit](../v1-sprints/017-f0-sequencing-audit.md). +B03–B06 CPU architecture construction may proceed while B02/F0.3 remains open. +This changes order only: all R1–R9/C1–C7/A1–A13 scope, thresholds, source and +protocol obligations remain required. No CPU interface freeze, formal agent +comparison or quality/speed/v1-completion claim is permitted through this overlap. +B03 starts with independent ownership, weight/offset, transaction and persistence +checks; B06 must probe Formula and heterogeneous sequential runs before stabilization. +See [Sprint 018](../v1-sprints/018-b03-cpu-state.md) for execution and acceptance. + +### F1: CPU foundation, close a small path then all v1 coverage + +- [ ] **F1.1: Data/run/artifact semantics.** Explicit device/run identity, unique weight ownership, + parameter/model axes, propose/commit/reject, RNG, input ownership and versioned models. Use F0 state + fixtures, not wholesale old-model migration. +- [ ] **F1.2: Composable trees.** Aggregate, candidates, score/feasibility, partition, leaves, predict; + two growth policies as Python. Start weighted numeric scalar constant leaves, then missing/CPU + categories. Independent symmetric growth reuses statistics/routing, not three cloned builders. + Extra information mass flows through the same pipeline, without trainer task-name branches. +- [ ] **F1.3: Two complete small recipes.** Standard second-order GBDT and two-parameter Normal, + including fixed steps and accept/reject. At least two rounds, intermediates, predictions, new-format save/load. +- [ ] **F1.4: Two early structural probes.** Formula two rounds with structure/links; train-many + M=1/2 then8 with independent state, different stopping and one failed run. Failure cannot contaminate + others or disappear from summaries. Reorder stable run IDs to verify invariance. +- [ ] **F1.5: Application data/target contracts.** Connect class/link, query/pair, residual/weight, + vector outputs, positive-target units/offset, AFT events/censoring and distribution/formula inputs/outputs. + F0 counterexamples check indices, acceptance, inference/round trips. If generic runner needs task-name + branches, fix boundaries first. Commit recipes separately; unsupported AFT censoring explicitly fails. +- [ ] **F1.6: Complete CPU coverage.** Binary/multiclass, ranking, weighted quantiles, Gamma/Tweedie + means and vector leaves in independently verified commits. Cross-process persistence covers + new state, categories/output/inference. Complete R1–R9/C1–C5 CPU cells and local C6/C7 entry points; + author comparisons and clean-environment delivery remain F2/F5. Every A1–A13 links to runnable + CPU recipes/workflows, not merely cards or illustrative demos. + +F1 exit: required recipes compose the same semantic components. If Formula/train-many bypass core +state, redesign before freezing interfaces; do not defer them. No full feature/speed parity promise +with XGBoost, LightGBM or NGBoost. F1.5/F1.6 must pass before CPU interface freeze. Broader censoring +and real quality remain separate. Acceptance: CPU E0, E1, E2; F1.1–F1.3 alone is only a minimal architecture +milestone. Exploratory F2.1 may start after F1.3 to catch usability problems early; formal F2.2 waits +for required CPU capabilities and frozen interfaces/judges. + +### F2: Agent algorithm-change experiments before stabilizing interfaces + +- [ ] **F2.1: Exploratory trials.** Install wheel, perform F0 tasks in independent directories, + record assistance, core/private imports, failures/fixes. Redesign is allowed; these trials are not scored. +- [ ] **F2.2: Frozen comparisons.** Same agent/tools/budget; OpenBoost versus appropriate incumbent + hooks and, when needed, source edits. Optional NumPy cost reference. At least three independent + attempts, rotate order; record setup, active time, tokens/compute, human hints, correctness and code + understood, not LOC alone. +- [ ] **F2.3: Held-out tasks.** Use tasks excluded from F1 redesign. If used to change interfaces, + reclassify as development. Candidates cannot overwrite verifiers/final evaluation. Small samples + are directional evidence, not statistical significance or external adoption. + +F2 exit follows E5: five development types, two held-outs, fixed attempts/budgets, correctness and +at least two deep-change cost wins, with control results retained. If advantage is only docs/prompts, +fix docs and rerun fairly. Repeated core edits indicate poor abstraction, not something extra wrappers +automatically solve. Rerun affected evaluation after semantic changes. + +### F3: Make proven compositions work on one GPU + +- [ ] **F3.1: Device-resident vertical path.** Implement bulk ops under F1 semantics, verifying + gradients/curvature, stats, splits, leaves, round updates, quality/inference end to end. First reproduce + P7 with its original threshold; score new recipes under separate F0 protocol. +- [ ] **F3.2: Extensions actually execute on device.** At least one nondefault F2 component and + multiparameter update run on GPU. Expose fallback/sync. Count dynamic backtracking synchronization; + do not substitute fixed steps. +- [ ] **F3.3: Compatible train-many groups.** After sequential parity, test M=1/8/32 reuse/batching. + Group different parameter counts/round budgets; fusion need not support every recipe. Record + throughput, latency, memory limits, failures, JIT/transfers/end-to-end quality. Single GPU first, no Ray/multi-GPU. + +F3 exit: readable/optimized forms of the same algorithm agree and meet frozen workload budgets. +Use authorized Modal for bounded timed runs with full provenance. Failure may retain CPU research +value but cannot support GPU cost claims; do not hide structural overhead by deleting verification. +Complete required R1/R4/R5/R6/R8 CUDA subsets, R9 batching and E4. Kernel speedup cannot replace +performance gates or all synchronization/transfer costs. Optional CUDA recipes list CPU results/device status separately. + +### F4: Real use cases and independent adoption + +F4 need not wait for all F3. Prepare CPU trial material after F1; authorized external trials may +start after F2 interface revisions. Actual user obstacles may change GPU optimization priorities. + +- [ ] Complete E3 for every A1–A13, with raw artifacts and at least 6 independent sources. Evaluate + Poisson/Gamma/Tweedie separately; proper distribution scores, Formula counterexamples plus real + tasks, and complete model-selection quality/cost. Use strong baselines, preregistered metrics, + independent test, repeated seeds/folds and all failures. No representative-only subset or synthetic-only Formula. +- [ ] Supply installable wheels, recipe sources, minimal reproductions, component contracts and + diagnostic examples. Third-party-defined tasks are more valuable than more self-authored demos. +- [ ] After user-authorized contact, at least two external authors try it; one implements their + own method and reuses it on another task. Record completion, obstacles/reuse reasons. No current + external-trial/retention evidence exists. + +F4 has separate exits: E3 engineering quality and E7 repeated independent use/reproducible benefits. +Internal agent success is not external adoption. Do not wait for an entire ecosystem/all models/ +perfect benchmarks to prepare trials. Unauthorized outreach is not a blocker for other engineering; +E7 must pass before claiming adoption validation. + +### F5: Stabilize product boundaries from evidence + +- [ ] Stabilize repeatedly used contracts only; organize independent recipe packaging/discovery. +- [ ] Remove unused rewritten paths/duplicate concepts. Migrate correctness cases to new semantics, + not old import/signature/format compatibility. Historical revisions reproduce old behavior. +- [ ] Verify install→baseline→algorithm change→verification→save/inference and update public docs. + README cannot advertise new architecture before implementation passes. + +### v1 completion and dependencies + +Main dependency: F0→F1→F2→F3→F5. F4 baseline preparation starts in F0, formal quality follows stable +recipes, and external trials proceed independently after F2. Phase commits link E-gates, raw records +and incomplete cells. Engineering v1 requires individual A1–A13 evidence and every required E0–E6 +pass. E7 is separate for external adoption/impact claims. Unfinished cases stay unfinished, not +renamed later cases to declare completion. Revisit task/component reuse at phase exits, not just function counts. + +New code uses public modules/ordinary functions: data/targets, stats/ops, tree, objectives, runtime, +recipes, artifacts/models. See construction design for dependencies. Replace private modules as +needed; do not permanently maintain two trainers. Tests live in tests/v1, evaluation in benchmarks/v1. +Reproduce old baselines from fixed historical wheels/revisions without sacrificing the redesign. + +## 6. Execution discipline and stop conditions + +No compatibility requirement does not demand indiscriminate rewriting. Architecture may start +fresh; reusing correct computation that fits new boundaries is optional engineering judgment. +No signature-preserving shims and no destruction of counterexamples, evidence, user edits or history +in the name of starting over. + +Breaking changes are allowed early; freeze a version for formal F2 comparisons. APIs need not +support arbitrary plugins forever, but every evidence artifact must identify exact versions. + +- Track expressiveness, correctness, runtime cost, agent-change cost and adoption separately. +- Control-only advantage may require merely better tutorials/adapters; investigate first. +- If every use needs specialized core code, narrow/redesign reusable boundaries and pause feature expansion. +- If quality improves without less implementation work, record an algorithm result, not foundation value. +- Persistent GPU budget overruns require residency/batch/sync diagnosis, not immediate multi-GPU expansion. +- If evidence cannot justify the next investment, commit facts/revised plan rather than more code as a substitute. + +Start each change with the smallest independent failure. Use uv tests/lint; public capability changes +also check docs/package. Update learning, inspect staged diff, commit. Migrate only affected semantics; +removed compatibility interfaces have no ongoing pass obligation. This planning round checks Markdown +links and git diff --check, not nonexistent runtime changes. No push, release, messages or leaderboard submission. + +Starting instructions for the next execution model: + +> Read AGENTS.md, this plan, evaluation, release review, application contracts and v1 planning learning. +> Read F0.1 cards, construction design and Sprint 010 exit. Execute B02/F0.3: freeze actual data/hashes, +> installed capabilities, budgets/held-outs and manifest/runner/judge in verified commits. +> Never use production objectives as the sole oracle, maintain old compatibility or rewrite kernels early. +> Report passes and unverified scope, preserve failures and all required cases. F1 follows B03–B10 +> component connections; cards, oracles or rewrapped old trainers are not an implemented foundation. diff --git a/planning/boosting-release-review-2026-09-05.md b/planning/boosting-release-review-2026-09-05.md new file mode 100644 index 0000000..e088d8b --- /dev/null +++ b/planning/boosting-release-review-2026-09-05.md @@ -0,0 +1,92 @@ +# XGBoost / CatBoost / LightGBM: Release and plan review before v1 design + +Review date: 2026-09-05. This is a snapshot of public primary sources; these new versions +were not installed or run. Formal comparison must pin versions, wheel/build hashes, CUDA +variants and effective configuration. Old search snippets, latest development docs and +unmerged PRs do not establish shipped capabilities. + +## 1. Latest stable releases + +| Project | releases/latest on review date | Release record and recent changes | Implication for OpenBoost v1 | +|---|---|---|---| +| XGBoost | **3.4.1**, tag v3.4.1,6fe8c54 | Fixes categorical-container model slicing and JVM sparse batch prediction; notes dated2026-08-14, GitHub shows Aug15 08: 30 | Category encoding, slicing and saved-model inference are correctness obligations; retain both dates without equating documentation and upload times | +| CatBoost | **1.2.10**, tag v1.2.10, b1bd2a6,2026-02-19 | JVM transposed prediction, Spark4.0/4.1; adjacent1.2.9 contains major recent Python/data-interface changes | Last-patch-only reviews miss input/inference capabilities; compare complete installed versions | +| LightGBM | **4.7.0**, tag v4.7.0,8f7036f, GitHub shows Jul18 20: 20 | Polars/Arrow, ROCm/HIP, NCCL multi-GPU, CUDA13 builds, distributed fixes; organization lightgbm-org, default branch main | Do not claim LightGBM lacks CUDA/multi-GPU; record CPU/OpenCL/CUDA/HIP separately | + +Sources: [XGBoost 3.4.1](https://github.com/dmlc/xgboost/releases/tag/v3.4.1), +[CatBoost 1.2.10](https://github.com/catboost/catboost/releases/tag/v1.2.10), +[LightGBM 4.7.0](https://github.com/lightgbm-org/LightGBM/releases/tag/v4.7.0). +LightGBM retains the displayed month/day; no exact UTC timestamp is inferred from an unavailable API. + +### Structural XGBoost 3.4 changes + +Upstream calls3.4.0 hist vector leaves feature-complete while still experimental, covering +categories, constraints, DART, distributed execution, model inspection and batch statistics. +MAE/quantile leaf estimation changes to smooth approximations. Default binaries use CUDA13.3; +xgboost-cu12 uses CUDA12.9. [Official3.4 notes](https://xgboost.readthedocs.io/en/stable/changes/v3.4.0.html). + +Design inference: multioutput/vector leaves are not an exclusive differentiator. Separate split +statistics from leaf-fitting statistics and allow leaf algorithms to change. Old quantile math +cannot serve as the latest-version oracle. Preflight coexistence of old T4 environments and +new wheels; never silently downgrade competitors or fall back to CPU. + +### Do not underestimate existing CatBoost capabilities + +1.2.9 adds Polars inputs with auxiliary fields, RMSPE, mmap model loading, Python 3.14 support, +and Lossguide/data-initialization improvements. These are shipped notes, not locally reproduced +speed claims. [1.2.9 release](https://github.com/catboost/catboost/releases/tag/v1.2.9). + +GPU custom objectives/metrics appear in1.2.6. Python custom GPU losses are not unique to OpenBoost; +verify task-specific scope and calling conventions. [1.2.6](https://github.com/catboost/catboost/releases/tag/v1.2.6). + +Official objectives include Poisson, Tweedie, MultiQuantile, RMSEWithUncertainty, Cox and SurvivalAft, +with per-objective device boundaries; Cox/SurvivalAft are CPU. AFT upper-bound sentinels differ +from XGBoost and require semantic adaptation. [Objectives/devices](https://catboost.ai/docs/en/concepts/loss-functions-regression). +Ordered boosting/category statistics are established techniques to review, not a full v1 replication +target. [Official references](https://catboost.ai/docs/en/concepts/educational-materials-papers). + +### LightGBM task breadth and engineering + +Public parameters cover classification, multiclass, ranking, Poisson/Gamma/Tweedie, quantiles, +constraints and custom objectives. Public interfaces support per-round updates and leaf-output +modification. [Parameters](https://lightgbm.readthedocs.io/en/stable/Parameters.html), +[Booster](https://lightgbm.readthedocs.io/en/stable/pythonapi/lightgbm.Booster.html). +Compare corresponding objectives and full preprocessing/inference, not one regression fit timer. +The4.7 weighted-percentile fix reinforces the need for independent nonunit-weight leaf references. + +## 2. Public plans by evidence level + +Do not imply all libraries have a unified roadmap with committed delivery dates. Distinguish +formal trackers, explicit author intent, proposals and community requests. States reflect the +review date and do not guarantee scheduling. + +| Project/source | Observed status/content | Use for v1 | +|---|---|---| +| XGBoost [multioutput roadmap#9043](https://github.com/dmlc/xgboost/issues/9043) | Open, type: roadmap; body updated for3.4 hist completeness; discusses multitask, broader outputs/execution interfaces | Verify completed items in releases; old checklists are not all missing features. Separate model/output/statistic axes | +| XGBoost [default-parameter RFC#12131](https://github.com/dmlc/xgboost/issues/12131) | Open; 2026-03-26 proposes learning-rate, sampling, budget changes | Proposals are not defaults; record effective defaults and fair tuned configurations | +| LightGBM [#2302](https://github.com/lightgbm-org/LightGBM/issues/2302) | Official feature-request/voting hub, no fixed delivery commitment; linked items vary in status | Suggests loading, routing, ranking, memory pain points; votes are not adoption or roadmap commitments | +| LightGBM [prediction efficiency#7326](https://github.com/lightgbm-org/LightGBM/issues/7326) | Open, assigned author explicitly plans Python predict/import cost research | Measure process startup, import, load, single/batch prediction, not only warm fit | +| LightGBM [category encoding#7361](https://github.com/lightgbm-org/LightGBM/issues/7361) | Open proposal for container-independent mappings/cross-language state; no milestone | Category semantics, persistence and unseen behavior must not depend on pandas internals | +| CatBoost [exported-model CI#3173](https://github.com/catboost/catboost/issues/3173) | Open Task, 2026-08-23; improve exported-code tests; links#3174 | Visible engineering task, not a whole roadmap; verify inference artifacts in independent environments | +| CatBoost [C++ export compatibility#3172](https://github.com/catboost/catboost/issues/3172) | Open, 2026-08-23; language-standard issue for categorical export, no release commitment | Deployment is real usage; OpenBoost still need not preserve old APIs/formats | + +No current unified, time-committed CatBoost roadmap was found after reviewing releases, tasks, +contribution material and milestones. Long-lived planned/good-first-issue labels do not mean +imminent release. LightGBM's request hub likewise does not schedule every request. GitHub API +and some filtered pages failed; conclusions use successfully read releases/specific issues, +not an exhaustive review of comments, PRs or private plans. + +## 3. Decisions carried into v1 + +1. **Target verifiable algorithm changes.** Classification/regression, probabilistic models, + GPU custom losses and vector leaves already have alternatives; include them as controls. +2. **Evaluate algorithms and applications separately.** All A1–A13 are required; do not select + only insurance/AFT or a few representative cases. +3. **At least five modification tasks:** objective/geometry, split/growth, leaf solver, update + control, run scheduling, including an existing-library-friendly control and unseen tasks. +4. **Include actual engineering cost.** Categories/missingness, weights, offsets, groups, persistence, + cold startup and inference need independent checks. Library GPU support is not per-objective support. +5. **Freeze competitors/protocol versions.** Capability smoke first; record package/version/hash, + backend/driver, effective parameters and inference semantics. New releases require a new experiment version. + +This review supports design/baseline selection, not OpenBoost speed, quality or adoption superiority. diff --git a/planning/foundation-application-contracts.md b/planning/foundation-application-contracts.md new file mode 100644 index 0000000..5b9816b --- /dev/null +++ b/planning/foundation-application-contracts.md @@ -0,0 +1,143 @@ +# OpenBoost v1 application coverage and case contracts + +Date: 2026-09-05. Status: user-required design/acceptance scope, **not implemented architecture**. +Complements the [main plan](agent-boosting-foundation-plan.md) and +[E0–E7 evaluation](openboost-v1-evaluation.md); code-review baseline f30c2ed. +F0.1 [task cards](foundation-tasks.md) select data, splits and mathematics; at this design baseline, +downloaded hashes, actual baseline runs and v1 implementation remain incomplete. + +The user explicitly requires **every following application, with individual delivery and acceptance**. +Each has target semantics, recipe, real workflow and independent evidence. Insurance/AFT have no +special status. Keep foundation-first design with no backward-compatibility requirement. + +## 0. Required A1–A13 matrix + +Every row is required, not a menu. F0 may choose concrete sources but must freeze versions/hashes, +licenses, splits, feature availability and task definitions. Source choice never removes a required +application. Compose shared components rather than copying industry-specific trainers. + +| ID/recipe | Required application | Data and behavior | Independent acceptance/main evaluation | +|---|---|---|---| +| A1/R1 | Continuous regression | Frozen California Housing or another real source; train, predict, save/load | Numeric/missing, weights, generalization, RMSE; state geographic limits | +| A2/R1 | Binary classification | [UCI Adult](https://archive.ics.uci.edu/dataset/2/adult); categories/missing/probabilities | Mapping, unseen policy, class weights/link; log-loss, auxiliary AUC | +| A3/R1 | Multiclass | [UCI Covertype](https://archive.ics.uci.edu/dataset/31/covertype); class mapping, K parameters/probabilities | Softmax, normalization, per-class quality, multi-logloss; threads/GPU memory | +| A4/R3 | Group ranking | [Microsoft MSLR](https://www.microsoft.com/en-us/research/project/mslr/); pairwise and NDCG-weighted lambda recipes | Query/pair dependencies, official folds, NDCG@10; retain usage terms, no cross-query splits | +| A5/R2 | Quantile regression | [UCI Bike Sharing](https://archive.ics.uci.edu/dataset/275); declared quantiles/weighted leaves | Temporal split, weighted pinball; exclude target components such as casual/registered and check feature availability | +| A6/R8 | Multioutput | [UCI Parkinsons Telemonitoring](https://archive.ics.uci.edu/dataset/189/parkinsons%2Btelemonitoring), two UPDRS targets; independent trees/shared vector topology | Subject splits, original score/interpolation semantics, per-target errors; benchmark accuracy is not clinical validity | +| A7/R4 | Event counts/frequency | Poisson+exposure; count and unit-exposure rate | Offset/weights once, units, Poisson deviance, exposure scaling | +| A8/R4 | Positive amounts/severity | Gamma on severity; declare claim-level or policy-average target | Positive support, selection, claim weights, Gamma deviance; A7 cannot substitute | +| A9/R4 | Aggregate loss/pure premium | Linked frequency/severity; Tweedie and frequency×severity workflow | Zeros, power, aggregate/annualized units, Tweedie deviance; A7/A8 cannot substitute | +| A10/R5 | Censored survival/AFT | Veterans' Administration lung cancer; fixed-scale log-normal event/right-censor workflow | Censored likelihood, risk/time/survival outputs, censored NLL and applicable probability/ranking metrics | +| A11/R6 | Distributional/NaturalBoost | Real regression or official ScoringBench subtask; two-parameter Normal and direction/step variants | Proper scores, Fisher/ordinary directions, links, calibration/width; coverage alone insufficient | +| A12/R7 | Structured/FormulaBoost | UCI Concrete; recipe Z, age x, two-parameter saturation hypothesis plus recovery/misspecification experiments | Independent Jacobian/directions, nonidentifiability, real quality/structural baselines; synthetic cannot replace real | +| A13/R9 | Model selection/train-many | Multiple configurations/objectives/groups on real tasks; M=1/8/32 and selected model | Prepared identity, independent seed/stop/failure, selected quality, full-set/selection cost | + +Judge by each A-ID, not a minimum task count. Require at least 6 independent sources; multiple targets +on one source count once, replicated rows do not create scale. If unavailable, F0 may choose a public +source with the same semantics and record why; the item remains pending until chosen. Never drop +cases or replace poorly performing datasets after evaluation. Each F0 card specifies data/target/output, +baseline, oracle, CPU/CUDA boundary, quality/cost, implementation phase and evidence path. +A7–A10 are expanded below; all rows share delivery/acceptance obligations. + +## 1. Insurance: Define frequency, severity and pure premium separately + +| Task | Target and units | Initial strong baseline | Required semantics | +|---|---|---|---| +| Frequency | Claim count over coverage period; unit-exposure rate | Poisson GLM; XGBoost `count:poisson` | Count+log-exposure offset or verified rate+exposure weights; define separately, never double exposure | +| Severity | Positive eligible incurred claim payment or policy average per claim | Gamma GLM; XGBoost `reg:gamma` | Claim-level and policy-average weights differ; record zero payments, counts and selection | +| Pure premium/total loss | Expected loss per exposure or expected period total | Frequency×severity, XGBoost `reg:tweedie`, Tweedie GLM | Distinguish annualized/aggregate/zero loss; freeze power, offset/weight and units | + +XGBoost demonstrates log-exposure through base_margin: +`E[count | X, e] = e * exp(F(X))`. Baselines must supply corresponding offsets during train, +validation and prediction; rate output must clarify exposure=1. +[Official offsets](https://xgboost.readthedocs.io/en/stable/tutorials/intercept.html#offset). +Check actual Poisson/Gamma/Tweedie settings against pinned versions. +[Parameters](https://xgboost.readthedocs.io/en/stable/parameter.html). + +Candidate sources: freMTPL2freq/freMTPL2sev, OpenML41214/41215. The scikit-learn example is a +starting point for joins, targets and GLM comparisons. Its clipping/zero handling are processing +choices, not unexplained raw-data truths. +[Official insurance example](https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html). + +F0 freezes source/version/hash, claim-policy joins, exclusions/clipping, missing/categories and splits. +Related policy/entity records cannot leak across train/test. Use temporal splits if valid time +information exists; otherwise state the limit, never invent temporal validation. Fit encoders/bins on training only. + +Two acceptance layers: + +- **Math/workflow:** nonunit exposure, nonunit/zero weights; offset and weight each apply once. + Doubling exposure for fixed Poisson model doubles count but not rate. Validation, early stopping + and loaded inference retain the same definitions. +- **Application:** appropriate deviance in matching units, totals and predefined group predicted/actual + comparisons. Full distributions additionally need NLL/CRPS/tail metrics. Mean-only Gamma/Tweedie + objectives do not imply calibrated distributions; exposure-scaled means do not prove aggregation laws. + +Deliver A7, A8, A9 separately. Dependency order is allowed; Poisson success does not complete Gamma, +Tweedie or composition workflows. + +## 2. Survival/AFT: Observations are not always event times + +XGBoost `survival:aft` uses lower/upper labels for complete, right, left and interval censoring, +with declared noise family/global scale. Use the pinned version's supported entry point, e.g. +DMatrix label_lower_bound/label_upper_bound. +[Official AFT tutorial](https://xgboost.readthedocs.io/en/stable/tutorials/aft_survival_analysis.html). + +| Observation | Labels | Continuous-time likelihood contribution | +|---|---|---| +| Event at t | lower=upper=t, t>0 | Density f(t) | +| Right censored | lower=t, upper=+inf | Survival S(t) | +| Left censored | lower=0, upper=t | F(t) | +| Interval censored | 0 Recipe[Python recipe / custom training loop] + Model[Convenience fit / predict] --> Recipe + Recipe --> Obj[Objectives, geometry, directions, acceptance] + Recipe --> Tree[Readable growth and leaf solving] + Tree --> Ops[Bulk statistics, candidates, routing, prediction] + Obj --> Ops + Ops --> CPU[NumPy / CPU kernels] + Ops --> GPU[CuPy / CUDA kernels] + Recipe --> State[Explicit data, run state, proposal commits] + State --> Artifact[Inference model and new format] +``` + +The torch analogy is public computation, explicit state, ordinary composition and device choice. +v1 specializes in boosting statistics, routing, weak learners and iterative updates; general autograd, +arbitrary-Python compilation and scheduling-graph compilers are not prerequisites. + +### Modules and dependency direction + +Planned public modules under src/openboost, created only when a build step has a real consumer, +not as empty scaffolding. Prefer small dataclasses, arrays and function arguments. + +| Module | Contents | Dependencies/change surface | +|---|---|---| +| data/, targets/ | Feature schema, fit/transform, prepared data; classification/interval/query/structure | Basic arrays/execution context only, not recipes | +| stats/ | Fields, weight semantics, Newton/direction-regression adapters | External functions can add statistics; beyond G/H | +| ops/ | Public bulk signatures and CPU/CUDA dispatch | No task names/model classes; private kernels allowed | +| tree/ | Candidates, score/feasibility, three growth policies, leaves, topology/payload | Uses stats/ops; grow Python is public | +| objectives/ | Loss, links, gradients, Fisher/GGN, directions | Can read whole Problem; not necessarily row-independent or per-parameter trees | +| runtime/ | Device/stream/workspace, RNG, transactions, run collections | Resources/commits, not one loop or recipe imports | +| artifacts/ | Inference models, output transforms, versioned readers/writers | Independent of training objectives/builders; declared inference dependencies | +| recipes/ | Runnable R1–R9 ordinary functions/reference compositions | Uses the same public interfaces as external packages | +| models/ | Optional fit/predict convenience APIs | Delegates to recipes/inference; no copied algorithm logic | + +Independent mathematics lives in tests/v1/reference with no production imports. Real data, adapters, +runner/judge live in benchmarks/v1. Avoid self-referential tests and benchmark-specific core branches. + +## 2. Data and state without inherited container restrictions + +### Minimum public records + +| Record | Required contents | Ownership/invariants | +|---|---|---| +| PreparedData | Row IDs, feature schema, bin state, codes/missing, identity, device | prepare/transform owns storage; training read-only; validation reuses training transformer | +| Problem | Prepared data, typed target, weight, offset, query/structure, output schema | Roles aligned by row ID; target types validate support; auxiliary fields are not automatic features | +| Geometry | Raw snapshot version, unweighted derivatives, curvature kind/shape or solver, loss | Distinguish exact Hessian, diagonal bound, Fisher, GGN; no universal N×K×K allocation | +| FitRequest | Split fields, leaf fields, row context, learner output schema | Split/leaf may differ; not an ambiguous grad/hess tuple | +| TreeModel | Topology, conditions, scalar/vector payload, output width | Variable nodes; immutable after commit; scratch cannot overwrite history | +| AcceptedState | Train/validation raw[N, K], base, terms, version, best/stop | Replaced only by successful commit; offset not accumulated repeatedly; K is within-run parameter axis | +| Proposal | Parent version, one/more terms, raw delta, trial coefficient | No precommit mutation; joint parameter updates are one transaction | +| RunContext | Run ID, seed, round, device/stream, workspace, execution record | Explicit, no global backend, no shared mutable run state | + +These are semantic boundaries, not eight inheritance layers. Geometry→FitRequest adaptation is +algorithm code authors can write as functions. External code may bypass high-level records to call an operation. + +### Initial binning and identity + +- External raw features are[N, F]. Initial CPU codes are feature-major[F, N]int32 with separate + boolean missing mask/category dictionaries. Regular codes start0; 255 is not public semantics. + F3 may pack uint8/uint16 with explicit missingness and conversion costs. +- fit_binning sorts finite training values and uses explicit unweighted quantiles; default at most254 + regular bins is configurable. Merge duplicate cuts; constant columns have one bin; all-missing + columns have no candidates. For B bins, interpolate empirical quantiles j/B, j=1..B-1; retain unique + min<=cut0 until depth/leaf/resource + budgets. Unsplit nodes are leaves; within-level budget conflicts use fixed gain/ID order. +- **Best-first:** heap of best splittable-leaf candidates, maximum net gain first, ties by node/candidate + ID. Recompute new children only; unaffected candidates remain with routing-version references. +- **Symmetric:** common feature/condition/missing direction per level. Initially require legality + in every leaf. Align common candidate IDs, AND validity, sum gains, then choose; never combine node + winners. Stop the whole level if none; budget must fit the entire next level. This explicit + semantics does not promise every CatBoost training detail. + +Start numeric/scalar, then complete extra information, missing, categories/vector leaves through the +same entries. Task-name branches or bypassed supplied functions prevent acceptance. + +## 5. Trees, parameter mappings and proposal transactions + +Compact arrays: left/right, feature, split_kind, numeric cut or category-set reference, missing_left, +leaf_index. Allocate continuous nodes with explicit children; initial int32 indices reject capacity +overflow before allocation. No2*i+1 requirement, fixed511 nodes or depth8 format limit. Separate +[n_leaves, L] payload. Symmetric trees may compress storage while preserving export semantics. +Grow capacity in blocks, trim on finalization. CUDA buffers remain device-side until checkpoint/export. +Conditions bind transformer versions; prepared inference verifies schema/cut identity, raw X uses +saved transforms. Equal integer bins from different transformers are not interchangeable. + +Terms contain learner+coefficient+output_mapping. Initial mappings support column selection and +explicit[L, K]matrices. Usually leaves are unshrunk and learning rate appears once in coefficient. +Scalar trees can update one parameter, vectors jointly update K. Formula/link are transforms above +raw, not part of generic tree prediction. + +State: accepted(v)→proposal(parent=v)→evaluate→accept/reject. + +1. Compute geometry from accepted raw, fit learner, predict base delta. Backtracking changes alpha + and reuses trees/deltas rather than retraining. Train/validation caches are separate. +2. Candidate raw lives in scratch. Stored raw contains base+learner sums; objective/output temporarily + applies offset, never accumulates it each round. +3. Before acceptance, finish candidate prediction, finite/shape checks and algorithm-specific loss + checks. Then switch raw buffers and append terms/coefficients/version. Reject stale parents. +4. Rejection frees scratch without appending trees/coefficients or changing best/accepted raw. + Fixed steps use the same transaction, but need not decrease training loss every time. +5. Joint parameters accept/reject together. Ordered updates create versions per parameter, next + reads latest accepted state. Track outer rounds/substeps separately; D4 has at most6 trials. +6. Best snapshots retain term cutoff, coefficients/mappings and required state. Restoration may + recompute raw; never trim trees while leaving future coefficients/stale caches. + +Inputs are read-only, scratch exclusive, committed trees stable. CPU ownership/read-only views and +CUDA explicit lifetimes/single writers enforce this. Development conformance uses checksums/injected +reuse to detect mutation; production boundaries check shape/device/indices/numerical state without +copying all inputs every round. Observe async CUDA errors at transaction completion before reporting +success; count synchronization in E4. + +## 6. Composing every application + +| Application/recipe | Changing algorithm code | Shared components | +|---|---|---| +| A1/R1 | Squared loss→Newton FitRequest→grow→fixed step | Stats, three growth policies, scalar payload, transactions/inference | +| A2/R1 | Logistic, class mapping, probability transform | Same grow/state, CPU categories/missing | +| A3/R1 | Softmax/diagonal bound, K trees from one snapshot, joint commit | Parameter axis, scalar terms, vector raw | +| A4/R3 | Query pairs/lambdas→rows, new ranking each round | Row stats, grow, seeds, score output | +| A5/R2 | Pseudo split fields, residual/weight leaves | Routed views, replaceable leaves, same prediction | +| A6/R8 | Independent trees/shared scoring, full K leaf fields | Separate split/leaf fields, vector payload, mapping | +| A7/R4 | Offset-aware objective/link, rate/count | Newton/grow, explicit target/offset binding | +| A8/R4 | Positive loss/derivatives, log-mean | Same scalar stats/grow/transaction | +| A9/R4 | Tweedie or paid-frequency×severity | Explicit weights/units, composed artifact, no extra trainer | +| A10/R5 | Intervals, event/right-censor likelihood, time output | Newton/grow, valid typed infinity | +| A11/R6 | Ordinary/Fisher→regression learners, joint/ordered/backtracking | Direction adapters, scalar/vector terms, transactions | +| A12/R7 | Structure, Jacobian/GGN, links/formula output | Same direction-fit/acceptance as Normal, declared inference dependencies | +| A13/R9 | Recipe/run lists, per-run stopping, compatible scheduling | Read-only prepared, independent contexts/state, same operations | + +Prove reuse through calls/tests, not names. A5 leaf changes must affect second residuals and loaded +predictions. A12 full/diagonal directions need no grow edits. D2 supplies external fields/feasible +functions without a core branch. + +## 7. CPU, CUDA and train-many construction + +### Implementations/device boundaries + +CPU production starts with NumPy vector operations; hot row reduction/routing may use existing +Numba CPU kernels. Oracles stay naive NumPy/loops without importing kernels. Pass float64 E1 +before declaring lower precision; speed never relaxes the original correctness path. + +Initial CUDA: CuPy owns arrays/streams, reuse kernels only after new-contract verification, new hot +kernels initially use numba-cuda. F3 profiling decides DSL changes. F0.3 pins dependencies/drivers; +this design does not claim execution. Preregister precision/tie bands under E1. + +- Explicit ExecutionContext(device, stream, workspace, limits). Host metadata; device codes, targets, + raw, stats, routing/leaves. Loops read only compact decisions/metrics when needed. +- Initial prepare may fit/transform on CPU then upload once with full costs recorded. This is not + mid-objective/tree fallback. Resident fitting cannot download large arrays per tree for CPU raw updates. +- score/feasible can operate directly on NumPy/CuPy arrays; per-candidate Python callbacks are not + automatically CUDA. Strict CUDA requests preflight-reject CPU-only components. +- Check actual components, dtype, layout, target/payload/update policies, not recipe class names. + Propagate device errors; never rerun CPU and report CUDA success. +- First optimize multiple nodes/candidate batches/learner groups. Reduce copies, allocations and + Python round trips before fusion. Optimized combinations record component/semantic versions; + replacements use supported public composition or explicitly fail, never bypass customization. +- Record transfer bytes, sync, JIT cold/warm policy, workspace peak and full fit/predict. Apply E1/E3/E4; + a fast histogram is not the foundation deliverable. + +### Multiple runs begin with correct sequential execution + +First implement run_many(specs, prepared, execution="sequential"): ordinary recipes with independent +raw, terms, budgets, best/stop/errors. Return records for all run IDs. Expected fault injection passes +when recorded correctly; actual required failures still fail the gate. + +Derive stable keys(seed, run_id, round, component, purpose) via explicit UTF-8/hash to NumPy seeds, +with F0.2 fixed fixtures. CPU/CUDA parity may use CPU-generated indices with upload costs rather +than assuming matching RNG implementations. Rejection does not consume future keys; same-step retry +uses the same key; attempt ID is logging only. + +Then group prepared identity, device, dtype, kernel/statistic layout/current stage. Different K may +group separately; M is not K. Scheduler batches explicitly exposed common stages, not arbitrary-Python +analysis. Use per-run offsets/active masks for variable nodes; stopped/failed runs retain records. +Reuse workspace by lifetime, not raw state. F3 compares sequential reuse and batching against +independent same-ID results before measuring M=1/8/32 full costs. + +## 8. Inference format and author workflow + +Initial format: JSON manifest plus non-object NPZ arrays; reader uses allow_pickle=False. Declare +version, feature/cut/category schema, base, topology/payload/coefficients/mappings, output schema/dependencies. +Check shapes, index ranges, acyclic trees, dtypes, finite values/required fields. Corrupt/old formats fail. + +Core reader supports standard raw trees/built-in output transforms, not saved training objectives, +callbacks or closures. Extensions explicitly provide custom formula/payload codecs/transforms; +callers install/select them. Missing dependencies fail, without automatic downloads/unknown closure +execution. CPU reads standard CUDA exports without CUDA/training plugins; GPU inference is separately +declared. A9 saves both submodels/composition; A7 saves exposure requirements. Resumable checkpoints +also need run/algorithm config/random state; inference artifacts do not imply arbitrary resume. +Reproducible inference and in-memory best restoration are initially required. + +Each component ships input/output examples, math/weight conventions, shape/dtype/device, ownership, +errors, minimal independent fixtures and a real consumer. Errors identify run/round/component, field, +expected/actual schema. Recipes are the reading entry; changing loss should not require runtime/kernel +study. Every application's workflow checks install→baseline→one change→two-round comparison→save/new-process predict. + +## 9. Build in independently acceptable commits + +This refines existing F phases, not another abstract roadmap. F0.2 can split by mathematics; +**actual foundation product implementation begins in F1**. B numbers identify construction slices only. + +| Slice/phase | Create/replace | Minimum acceptance/counterexample | +|---|---|---| +| B01/F0.2 | Scalar/tree enumeration, then geometry, typed data, transactions/runs | Hand mismatch fails; zero weights, ties, rejection, round2 gradients, all application formulas | +| B02/F0.3 | Manifest/adapters/runner/judge, data hashes, versions/budgets/held-outs | Missing/polluted/failed controls cannot pass; no premature benefits | +| B03/F1.1 | Numeric Problem, RunContext, AcceptedState, minimal artifact | Two IDs/read-only inputs; synthetic constant trees check accept/reject, offset/save without grow | +| B04/F1.2 | CPU stats/histogram/candidates/score/feasible/choose/partition/scalar leaves/depthwise | Hand two-level fixed-bin tree; replace feasibility for information mass; no old builder wrapper | +| B05/F1.3 | Complete squared/Normal Python recipes | Two rounds/intermediates/output/roundtrip; fixed/backtracking/full rejection; first complete foundation path | +| B06/F1.4 | Formula geometry/structure, sequential run_many | A12 two rounds; M=1/2/8, different K/stop/error; fix B03 if state bypass required | +| B07/F1.2, F1.5 | Best-first/symmetric, missing, CPU categories, class schema | Three independent topologies, both missing routes, unseen mapping; reuse B04 ops | +| B08/F1.5–F1.6 | Binary/multiclass, vector leaves, mappings | A2/A3/A6, joint K, separate sketch/full leaves | +| B09/F1.5–F1.6 | Ranking pairs/lambdas, quantile/penalized leaves | A4/A5/D3, routed residual/weight, round2/query isolation | +| B10/F1.5–F1.6 | Poisson/Gamma/Tweedie/AFT recipes/transforms | Individual A7–A10, offset/units/valid infinity, composed roundtrip | +| B11/F1.6, F2 | Complete A1–A13 workflows, external extensions, author experiments | CPU E0/E1/E2, D1–D5/H1–H2/E5; exploratory trials can start after B05 | +| B12/F3.1–F3.2 | Device ops/required recipes, nondefaults, multiparameter updates | R1/R4/R5/R6/R8 two-round/quality/persistence parity, visible sync, no bypass | +| B13/F3.3 | Compatible scheduler, batched histogram/route/predict, verified fusion | Same-ID M=1/8/32 independent results; E4 time/memory/failures | +| B14/F4–F5 | All real application artifacts, clean wheel/docs, unused-path removal | E3/E6; independent repeat use E7, not internal adoption | + +Split B07–B10 by recipe; large commits cannot hide failures. Formal B11 waits for required components. +Data/baselines start B02, real evaluation follows stable recipes, not first contact with data at B14. +Dependencies determine order, not whether every case must finish. + +### Old-code use and design stop conditions + +- Historical _core/_primitives.py and _core/_growth.py attempted components/multiple policies. + Preserve verified computation/counterexamples; public layers cannot merely wrap global backend, fixed G/H/layout/trainers. +- Experimental plugin precedence, scratch detachment, prediction-cache consistency and rollback + tests inform semantics. Recompute hand values for new weight/gain conventions, not copied old truth. +- User requested early production reset. F1 builds public entries without shims, dual writes or + two trainers. Historical baselines use fixed revisions/wheels, not current imports. +- If B05/B06 cannot share statistics/routing/state, fix that boundary. If B09 leaves only receive + G/H, fix row views. If GPU pulls full raw every round, fix residency/ownership before fusion. +- This design guides implementation; references, CPU/CUDA code, author benefits and all real quality + still require verification at this design baseline. The document itself passes no F1/E-gate and changes no thresholds. diff --git a/planning/foundation-tasks.md b/planning/foundation-tasks.md new file mode 100644 index 0000000..34afdb8 --- /dev/null +++ b/planning/foundation-tasks.md @@ -0,0 +1,446 @@ +# OpenBoost v1 F0.1: Algorithm task cards, alternatives and interface sketches + +Date: 2026-09-05. Code-review baseline: 7b57436. Status:**F0.1 specification; implementation, +independent references and formal evaluation were incomplete at the design baseline**. +Sources:[main plan](agent-boosting-foundation-plan.md),[applications](foundation-application-contracts.md), +[v1-plan-r2 evaluation](openboost-v1-evaluation.md). All A1–A13 are required; dependencies determine order. + +This file specifies tasks, mathematics, data, comparators and falsifiable results. F0.3 freezes actual +bytes/split hashes, wheels/drivers, 16 search configurations and budgets; none is claimed measured here. +F0.2 writes oracles; F1 production; F2/F3/F4 author/GPU/real-task evidence. Future test/module names +are deliverable contracts, not existing APIs. [Construction design](foundation-construction-design.md) +provides records, ops, tree/transaction/inference/device execution and B01–B14; cards do not replace it. + +## 1. Shared contracts and delivery matrix + +### Inputs, two-round state and failures + +- Row-aligned X[N, F], stable row_id[N], typed targets, optional weight[N], offset[N, K], query/entity IDs + and structure_input. Weights are finite, nonnegative with positive sum. Validate legitimate missingness/ + censor bounds separately from invalid NaN. Auxiliary fields never automatically become weights. +- C1 numeric/missing/CPU categories. Train-fitted mappings, dedicated missing state and declared + unknown-as-missing route are saved. Initial F1 categories use exhaustive one-category-vs-rest, + avoiding hidden target encoding. If quality fails, change the algorithm, not remove categories or relax E3. +- Tiny fixtures run at least raw0→geometry0→learner0→decision0→raw1→geometry1→learner1→decision1→raw2. + Compare intermediates, trees/coefficients, train/validation predictions and saved inference. + Correct shapes, first-round-only effects or stale gradients fail. +- Smooth objectives emit unweighted per-row g/h; statistics apply weight once. Name approximate + curvature separately. Direction-fitting for distributions/Formula separates direction from fit weights. +- Scalar Newton: v=-G/(H+lambda); gain is child sums minus parent of0.5*G²/(H+lambda), minus split penalty. + No implicit row-count scaling of lambda/penalty. Zero effective mass cannot split; invalid denominators fail. + Default fixtures: lambda1, eta.1, depth2, two rounds, all rows/features, no early stopping; special cases declare deviations. +- Gain ties follow(feature_id, candidate_id, missing_direction), with fixed CPU dtype/reduction order. + Three policies have independent topology checks, not identical-tree requirements. Symmetric chooses + common conditions by summed active-node gains; invalid candidates cannot win via negative/NaN sentinels. +- Rejection preserves accepted raw/trees/coefficients/best. Errors identify components/fields. + RNG follows stable run ID/seed/round/purpose, not Python hash() or scheduler position. +- Use E1 tolerances, E3 quality, E4 costs, E5 author trials, E6 installs. Real tasks record all splits; + selection reads validation only and retains missing/failing cases. + +### Implementation, devices and evidence + +All CPU cells are required. CUDA has explicit subsets and no silent fallback. Future runners index +reference groups, real cases and artifacts by these IDs. + +| Case | Recipe | Change/reference group | CPU phase | Required CUDA | Gates | +|---|---|---|---|---|---| +| A1 | R1 | Scalar regression/scalar, tree | F1.3/F1.6 | Numeric+missing | E0/E1/E2/E3/E4/E6 | +| A2 | R1 | Binary/category/classification, data | F1.5/F1.6 | Numeric encoding; native categories optional | E0/E1/E3/E4/E6 | +| A3 | R1 | Multiclass K/classification | F1.6 | Numeric softmax | E0/E1/E3/E4/E6 | +| A4 | R3 | Query/pair/lambda/ranking | F1.5/F1.6 | None; CPU required | E0/E1/E2/E3/E6 | +| A5 | R2 | Routed quantile/quantile | F1.5/F1.6 | None; CPU required | E0/E1/E2/E3/E6 | +| A6 | R8 | Shared vector topology/vector | F1.6 | Numeric squared error | E0/E1/E2/E3/E4/E6 | +| A7 | R4 | Count/offset/positive | F1.5/F1.6 | Numeric Poisson offset | E0/E1/E3/E4/E6 | +| A8 | R4 | Positive mean/positive | F1.5/F1.6 | None; CPU required | E0/E1/E3/E6 | +| A9 | R4 | Tweedie/composition/positive | F1.5/F1.6 | Poisson component per A7; whole workflow optional | E0/E1/E3/E6 | +| A10 | R5 | Censored likelihood/aft | F1.5/F1.6 | Numeric events/right censoring | E0/E1/E3/E4/E6 | +| A11 | R6 | Geometry/acceptance/normal, state | F1.3/F1.6 | Numeric Normal, both step policies | E0/E1/E2/E3/E4/E6 | +| A12 | R7 | Coupled formula/formula | F1.4/F1.6 | None; mixed execution separate | E0/E1/E2/E3/E6 | +| A13 | R9 | Isolated runs/shared data/runs | F1.4/F1.6 | One compatible R1/R4/R8 group | E0/E1/E2/E3/E4/E6 | + +Complete every required R1–R9 row. E7 external adoption is separate; internal success is not adoption, +and mathematical/real-task checks do not wait for outreach. + +## 2. A1–A13 task cards + +### A1: Continuous regression + +- **I/O:** X, real y, weights; real raw/predictions. L=(F-y)²/2, g=F-y, h=1; training weighted-mean base. +- **Data:** California Housing retains [historical archive/array hashes](../benchmarks/foundation/housing.json) + and units. Seeds0–2 existed; F0.3 adds3–4 using the same rule. Three splits are not five; random + splits do not establish geographic extrapolation. Sprint 013 supplies [five splits](../benchmarks/v1/datasets/housing.json) + with old hashes matching; licensing, budgets/real quality remain pending. +- **Algorithm:** scalar stats, three growth policies, Newton leaves, predict/add; baseline uses no + sampling, with separate row/column sampling seed checks. All policies compose ordinary Python. +- **Oracle:** weighted base, two-round G/H, best splits/leaves, missing routing, zero weights, ties/lambda + scaling. Constant targets cannot create NaN. Primary RMSE; stale next-round gradients fail even with valid shape. +- **Controls:** XGBoost `reg:squarederror`, LightGBM regression, CatBoost RMSE, with reasonable per-method + growth/leaf budgets and full preprocessing/inference costs. + +### A2: Binary classification and categories + +- **I/O:** two original labels, X, weights, saved label order. p=sigmoid(F), L=logaddexp(0, F)-yF, + g=p-y, h=p(1-p). Raw/probability/label are distinct. Reject single-class training; declare extreme-base clipping. +- **Data:** UCI Adult, unchanged official test; five seeds 0–4 stratified80/20 within official train. + ? is missing; strip test-label suffix dot. Exclude fnlwgt and do not infer weights from it. + Main real comparison has weight1; nonunit/class weights use independent fixtures. + Sprint 014 supplies [raw data/five splits](../benchmarks/v1/datasets/adult.json); encoding/capabilities/quality remain pending. +- **Algorithm:** CPU native categories and numeric encoding; GPU encoding is train-fitted with + dimensions, unknown semantics and costs recorded. Opponents may use native categories. +- **Oracle:** relabeling preserves probability meaning/model mappings; extreme logits stay finite; + no test-label encoding of missing/unknown; weight enters loss and leaf stats once. Primary log-loss, + auxiliary AUC/group confusion matrices. Sample/class weights do not guarantee calibrated probabilities. +- **Controls:** XGBoost `binary:logistic`, LightGBM binary, CatBoost Logloss. Record missing/category + processing and effective class weights in every artifact. + +### A3: Multiclass + +- **I/O:** class map, raw[N, K], stable log-softmax, probabilities/original labels. g=p-onehot(y), + exact H=diag(p)-ppᵀ; initial tree bound h_k=2*p_k*(1-p_k), explicitly not exact Hessian. Weight once. +- **Data:** UCI Covertype, all original numeric/indicator columns, labels0..K-1, seeds 0–4 stratified + 60/20/20. Do not silently compress soil/wilderness indicators into integer categories. +- **Algorithm:** all K directions from one raw snapshot, initially independent trees/joint commit. + K is output-parameter axis, not M runs; complete softmax updates feed the next round. +- **Oracle:** hand K=3, gradient rows sum0, probabilities sum1, exact matrix/bound separately checked, + class permutation reversible. Missing classes/invalid labels cannot be silently truncated. +- **Controls/quality:** XGBoost `multi:softprob`, LightGBM multiclass, CatBoost MultiClass; primary + multi-logloss and separate class metrics. Include Py-Boost for GPU/output-axis comparison. + +### A4: Group ranking + +- **I/O:** X, ordinal relevance, query boundaries, optional query/pair weights; within-query scores. + No generic row→pair-weight rule. Reject row weights or require explicit caller construction, never ignore them. +- **Data:** official five-fold MSLR-WEB10K train/vali/test. Missing feature tokens mean0 in dense + parsing, not missing. qid is not a feature; retain every real query. +- **Algorithm:** oracle enumerates same-query rel_i>rel_j pairs. L_ij=logaddexp(0,-(s_i-s_j)); + opposite row gradients, diagonal curvature sigmoid(d)*sigmoid(-d). Lambda multiplies frozen + abs(delta NDCG@10) from current ordering, without differentiating that weight or calling it ordinary + NDCG gradient. Initial scores0; mean pair loss per query then weighted query sum; no-pair queries + contribute zero. Sampling estimates/normalization are declared; changing pair count cannot silently change regularization. +- **Scale:** production may use bounded seeded per-query pair sampling; small fixtures enumerate + all pairs. Sampling count/normalization are search config and cost. gain=2^rel-1, discount=1/log2(rank+1), + stable row-ID ties, IDCG0→NDCG1 with separately reported count, not removed queries. +- **Oracle/controls:** no cross-query pairs, query score-shift invariance, sum pair gradients0, + two-round pair/lambda recomputation. Compare NDCG@10 with XGBoost `rank:pairwise`/`rank:ndcg`, + LightGBM lambdarank/rank_xendcg, CatBoost PairLogit/YetiRank. Use each opponent's weight semantics; + different optimizers are not numerical parity failures. + +### A5: Weighted quantile regression + +- **I/O:** real y, weights, q∈{0.1,0.5,0.9}; conditional quantile per q. r=y-F, pinball=max(q*r,(q-1)*r). + Pseudo split g=1[y=q*sum(w); filter zero weights, + use left ties. Add eta*leaf before new residuals. Initialize the same training-only weighted quantile. +- **Oracle:** [0,2,10], weights[1,3,1], q=.5 gives 2; weights can change optimum. At nonsmooth points, + check subgradient optimality, not second finite differences. Report each q's pinball and crossing + rate; independent models do not guarantee no crossing. +- **Controls:** XGBoost `reg:quantileerror`(current smooth objective), LightGBM quantile, CatBoost + Quantile/MultiQuantile. Align q/weights/output space; do not require smooth/new algorithms to match discrete oracle trees. + +### A6: Multioutput and vector leaves + +- **I/O:** X, Y[N, K], row weights, K=2 initially; raw/predict[N, K]. Train-only per-target mean/std, + saved inverse; leaf output schema separate from topology. Standardized base0; constant std1 plus flag. +- **Data:** UCI Parkinsons Telemonitoring motor/total UPDRS, both excluded from X; subject ID is + group-split-only. Subject-level60/20/20, seeds 0–4. Preserve score/interpolation semantics; no clinical-validity claim. +- **Algorithm:** both independent trees and shared vector topology required. Sum output gains; + leaves -G_k/(H_k+lambda). Also replaceable split-statistic projection, retaining full K leaf fields. +- **Oracle:** hand two-output split/leaves, K=1→A1, output permutation, projection cannot remove leaf + dimensions. Compare all targets over two rounds; one scalar channel or fabricated shared topology is insufficient. +- **Controls/quality:** XGBoost independent/experimental multi_output_tree, CatBoost MultiRMSE, + LightGBM target loop, Py-Boost native multioutput. Per-target original-unit RMSE and standardized + average; E3 checks every target, not averages hiding failures. Retain both OpenBoost structures' results. + +### A7: Counts and exposure + +- **I/O:** nonnegative integer count, e>0, independent sample weight. mu=e*exp(F); count mean or + unit-exposure rate; prediction declares e. L=mu-y*(F+log(e))+lgamma(y+1), g=mu-y, h=mu. +- **Data:** freMTPL2freq/OpenML41214; IDpol split-only, ClaimNb target. Do not copy example clipping + of ClaimNb/Exposure. Invalid e/weight/target are recorded and rejected or preregistered exclusions, + never silently changed during fit. Entity seeds 0–4. +- **Algorithm:** rate base=log(sum(w*y)/sum(w*e)); all-zero counts need explicit minimum-rate policy. + Offset is raw addition, not weight, consistent across train/validation/inference. +- **Oracle:** fixed F and doubled e doubles count, not rate; integer weights equal replication; + zero weight contributes no stats; saved model retains offset contract. +- **Controls/quality:** Poisson GLM; XGBoost `count:poisson` with base_margin=base+log(e), LightGBM + poisson with explicit init-score/prediction adapter, CatBoost Poisson/baseline adapter. Smoke + base/offset persistence/inference first; init_score is not automatically saved. Primary count + Poisson deviance, auxiliary rate/aggregate bias. + +### A8: Positive amounts and severity + +- **I/O:** y>0, weights, mu=exp(F)>0; Gamma mean with fixed shape1, L=y/mu+log(mu), g=1-y/mu, h=y/mu. + No fitted full-distribution claim. +- **Data:** freMTPL2sev/OpenML41215 joined to frequency via IDpol for covariates. Positive paid + claims, weight1, policy-group splits. Report nonpositive/orphan exclusions; do not mistake claims + for policy means. Share split IDs with A7/A9. +- **Algorithm:** base=log(weighted mean y), update log mean. Float64 references cover extreme y/mu; + no unrecorded clipping of production overflow. Errors locate offending component/input. +- **Oracle/quality:** independent derivatives, reject zero/negative y, two-round weighted leaves/ + saved predictions, declared amount units; primary Gamma deviance. +- **Controls:** Gamma GLM, XGBoost `reg:gamma`, LightGBM gamma. CatBoost's reviewed public list has no + Gamma; custom objectives are separate extension controls, not an invented built-in. + +### A9: Aggregate loss and pure premium + +- **I/O:** period positive-payment total c, exposure e, annualized y=c/e. Main path weight=e, + mu=exp(F) annualized mean, period prediction=e*mu. No additional log(e) offset. Extra business + weights require explicit products applied once. +- **Data:** frequency left-join severity, positive payments aggregated by IDpol. Zero only when + ClaimNb0 and no payments. Positive-count/no-payment and zero-count/positive-payment contradictions + are separate exclusions, not guessed missing=zeros. Target is positive paid loss, not net refunds. + F0.3 records join quality/counts. +- **Algorithm:** Tweedie p fixed per fit(default 1.5, search F0.3). + L=-y*mu^(1-p)/(1-p)+mu^(2-p)/(2-p), g=mu^(2-p)-y*mu^(1-p), + h=(2-p)*mu^(2-p)+(p-1)*y*mu^(1-p). + Also frequency×severity: Poisson counts **positive-payment records**, Gamma uses matching positive + payment means. Raw ClaimNb including zero payments cannot be multiplied by positive severity as an equivalent target. +- **Oracle:** zero loss valid; e enters weights/unit conversion, not offset again. Hand joins/ + aggregates/products, entity-order invariance, saved two-model dependencies/predictions. Both + Tweedie and composition need real results. +- **Controls/quality:** Tweedie GLM, XGBoost `reg:tweedie`, LightGBM tweedie, CatBoost Tweedie and + matching paid-count two-stage baselines. Primary exposure-weighted annualized Tweedie deviance, + auxiliary totals; means do not establish calibrated tails. + +### A10: Censored AFT + +- **I/O:** canonical(lower, upper), initial events/right censoring. Log-normal log T=F+sigma*Z, + Z~Normal(0,1), sigma default 1 fixed per fit. Raw log-time location; exp(F) median, + exp(F+sigma²/2) mean, plus survival/quantiles. +- **Data:** scikit-survival Veterans' Administration lung cancer, official Status/Survival_in_days; + event/right-censor stratification, seeds 0–4. Small real quality task, not scaling evidence. + F0.3 freezes training censoring estimate, IPCW grid/support. +- **Math:** z=(log(t)-F)/sigma. Event NLL=log(t*sigma)+z²/2+log(2*pi)/2, + g=-z/sigma, h=1/sigma². Censor NLL=-log(S_Normal(z)), g=-mills(z)/sigma, + h=mills(z)*(mills(z)-z)/sigma²; stable log-tail oracle. +- **Oracle:** event→censor changes loss/update; retain valid+inf, reject NaN/reversed intervals. + Explicitly reject unsupported left/interval/truncation. Two-round likelihood, units, monotone + survival/roundtrip; right-censor times are not deaths for RMSE. +- **Controls/quality:** XGBoost `survival:aft` Normal/same sigma, CatBoost SurvivalAft + dist=Normal; scale=sigma(CPU,+inf→-1), parametric log-normal AFT. Align density Jacobian/constants; + primary censored NLL, auxiliary IPCW Brier/C-index. No verified same-name LightGBM built-in; + custom-loss control may be separate. + +### A11: Distributional/NaturalBoost + +- **I/O:** real y, weights, raw=(mu, log_sigma), two parameters/Normal distribution. + L=log_sigma+(y-mu)²/(2*sigma²)+log(2*pi)/2; + ordinary g=((mu-y)/sigma²,1-(y-mu)²/sigma²), Fisher=diag(1/sigma²,2), natural=Fisher^-1*g. + Do not casually rename Fisher a Hessian. +- **Data:** same Housing inputs/splits as A1 but distinct distribution results and fixed units. + A1 RMSE cannot replace NLL/CRPS. +- **Algorithm:** independent parameter trees fit negative ordinary/natural directions, with explicit + fit/training weight relation. Both fixed and bounded backtracking required. Joint parameters + use one snapshot; D4 separately checks ordered updates. Train-weighted mean/scale, declared floor. +- **Oracle:** hand Fisher solve, two-round gradients/accepted state, no rejection residue; no duplicate + weighting in direction then tree regression. Independent NLL/CRPS evaluator. +- **Controls/quality:** NGBoost Normal+LogScore/natural, CatBoost RMSEWithUncertainty, global Normal, + outer-loop tree controls. Smoke variance/scale/raw conversion before comparison. Primary NLL, + auxiliary CRPS, coverage+width, PIT; coverage alone is insufficient. + +### A12: FormulaBoost and real structured tasks + +- **I/O:** trees read recipe Z only; structure x=age_days/28>0, target MPa. a=softplus(u), b=softplus(v), + f=a*(1-exp(-b*x)); outputs f, a, b. Saturating monotonicity is a **testable hypothesis**, not a source-proven physical law. +- **Data:** UCI Concrete Compressive Strength, seven material columns as Z, Age as x. Group identical + seven-column recipes for60/20/20, seeds 0–4; Age/target excluded from tree features. Duplicate identical + inputs stay grouped. F0.3 verifies group counts/age support. +- **Math:** stable -expm1(-b*x); J_u=sigmoid(u)*(1-exp(-b*x)), + J_v=a*x*exp(-b*x)*sigmoid(v). Half-square g=J*(f-y), GGN=JᵀJ with explicit solve damping. + Per-row rank<=1; full matrices do not establish identifiability. Default a0=max(weighted_mean(y_train),1e-6), + b0=1, stable softplus inverse; fixtures may declare raw0. Initialization cannot read validation/test. +- **Algorithm/oracle:** independent ordinary/diagonal/full directions, two parameters/two rounds, + correct line-search commits. Synthetic repeated Z/different x/known a, b plus single-x nonidentifiability + and misspecification. Real data evaluates predictions/constraints, not true-parameter recovery. + Without real results A12 is incomplete. +- **Controls/quality:** same global nonlinear formula, fixed-revision old Formula, outer coupled updates + with XGBoost/LightGBM/Py-Boost learners, and three-library ordinary regression with Z+x. Diagonal-h + interfaces do not prevent external full-direction solves. Primary RMSE, auxiliary in/out-of-support + errors/parameter stability. Do not reselect formulas from test; package inference dependencies explicitly. + +### A13: Train-many and model selection + +- **I/O:** prepared identity, stable run IDs, per-run recipe/config/seed/round budget, validation + metrics. Return all states/models/costs and validation-selected model, not an average table alone. +- **Data:** Covertype primary, Housing secondary; reuse A3/A1 splits. Share matching folds/bin settings + only; changed binning weights/mappings or folds invalidate reuse. +- **Algorithm:** M=1/8/32, sequential reference and compatible batching, independent early stop/best/ + seed/failure. CPU checks heterogeneous K/objectives; initial GPU group is compatible R1, not forced + heterogeneous fusion. Count all model-selection costs; 32-config E4 sets are not E3's16-trial budget. +- **Oracle:** independent versus sequential reuse versus batch by run ID; reorder/regroup invariance. + Inject failure without affecting others and retain it; ignoring failures cannot pass a required + ensemble. Selection reads validation only; committed/saved best states agree. +- **Controls/quality:** three-library loops with actual prepared reuse; Py-Boost GPU loop. Repeated + opponent binning versus our cache cannot be the sole speed claim. Selected model passes original + E3, all sets E4. Old ConfigBatch is semantic reference only. + +## 3. Alternatives audit: Hooks, devices and gaps + +Reviewed versions:[release review](boosting-release-review-2026-09-05.md), XGBoost 3.4.1, CatBoost 1.2.10, +LightGBM 4.7.0. **Documentation/code audit only**; all new-version runtime smoke is not_run, no CPU/CUDA +pass inferred. F0.3 pins NGBoost, Py-Boost, GLM/AFT dependencies. No valid Py-Boost latest release page +was obtained; do not invent a tag. + +| Path | Reviewed entry | Task/cost boundary | +|---|---|---| +| XGBoost | Built-in/custom objectives, grow policies, vector trees, outer rounds/source edits | Many A1–A10 tasks; 3.4 vector hist experimental. Hessian limits do not forbid outer preconditioning; count C++ build/debug cost | +| LightGBM | Objectives, Booster.update(fobj), rollback_one_iter, set_leaf_output | D1/D3/D4 may compose public hooks; replace leaves before next gradient. Per-candidate extra stats need source audit, total min_child_weight is insufficient | +| CatBoost | Native categories, symmetric/depthwise/lossguide, custom losses, pair/group targets | Strong A2/A4/A6/A11 controls; MultiRMSE GPU, MultiRMSEWithMissingValues not. SurvivalAft CPU; custom-loss GPU does not imply arbitrary algorithm GPU | +| NGBoost | Distribution/score/metric, natural gradients/training loop | Include A11/D4; geometry/line search are not inventions; measure actual policy-change cost | +| Py-Boost | Python/CuPy, callbacks, loss/metric, sampling, multioutput sketches | Direct foundation control, requires GPU. Applicable tasks must consider it; selected arm runs GPU while OpenBoost may begin CPU. Record E5 device costs; CPU unsupported is not failure | +| GLM/parametric AFT/global formula | Simple same-target statistics | Check real need for framework with matching units/links/inputs, not only weak default trees | + +F0.3 smoke checks each path's nonunit weights, prediction space, base/offset, save/load, final metric +and reported backend. Adapter failures are error and require fixing, not quietly relabeled unsupported. +GPU installation errors are environmental, not algorithm support. GPU/CPU author arms have equal +wall/token budgets with device costs separate and comparable tools/source access. + +Py-Boost DepthwiseTreeBuilder.build_tree uses multioutput_sketch for split G/H then original +grad/hess in calc_node_values. Separate split/leaf statistics are not unique. D2 source entry is +candidate selection in depthwise_grow_tree. D3 may use returned leaf indices, but leaf replacement +must update train/validation caches, not just exported models. This is call-path evidence, not +measured author time/runtime success. [Tree implementation](https://raw.githubusercontent.com/sb-ai-lab/Py-Boost/master/py_boost/gpu/tree.py). + +Keep shipped/roadmap/RFC/request distinctions. XGBoost multioutput, LightGBM prediction/mappings +and CatBoost export tasks inform design/risk, not unshipped runtime baselines. + +### Declared scope boundaries + +| Not currently required | Existing alternatives | v1 handling | +|---|---|---| +| Native CSR/CSC, external memory, distributed/multi-GPU | Three libraries have varied sparse/scaling paths; LightGBM 4.7 adds GPU capabilities | Single-device semantics first; explicit budgeted dense conversion counts cost; reject unsupported native parameters | +| All categorical CTR/ordered combinations | Mature CatBoost category processing | One explicit tested method first, while retaining real category quality gate | +| Full Cox/competing risks/truncation/all AFT censoring families | XGBoost/CatBoost Cox and multiple AFT labels | R5 events/right censoring; schema distinguishes/rejects others; no universal row-independence restriction | +| All distributions, DART/GOSS, linear leaves/all constraints | Upstream and historical capabilities for future comparison | Components over catalogs; typed payload/statistic hooks, accurate rejection | +| Arbitrary Python compilation/serialization | Python interfaces alone do not promise this | Explicit bulk device boundaries, formula dependencies, unsupported execution errors | + +These implement R/C boundaries without removing A1–A13. New requirements may revise scope, but +required cells cannot silently become optional after failed evaluation. + +## 4. D1–D5 author modifications + +Each task delivers an installed public-API extension/recipe without core/private edits; opponents +may use hooks, outer loops or source. E5 judges actual execution, not merely a custom-function call. + +| ID/axis | Change and independent oracle | Strong candidate alternative/cost | +|---|---|---| +| D1 objective/control | tau=.8 expectile, r=y-F, loss=abs(tau-I[r<0])*r²; analytic g/h, weighted base, two rounds/raw roundtrip | XGBoost/CatBoost listed expectile, LightGBM custom loss, Py-Boost loss. Built-ins allowed, no assumed OpenBoost win | +| D2 split/statistics | Each child has every declared cohort information mass>=1; extra sums separate from weights, best feasible ordinary gain, no split if none | Py-Boost build_tree→depthwise_grow_tree or XGBoost/LightGBM source. Total min_child_weight insufficient; any mathematically equivalent solution allowed | +| D3 leaf solver | Weighted pinball+lambda*(v-anchor)²/2, lambda>0; public routed residual/weight; new leaves affect next predictions | LightGBM routing/set_leaf_output, Py-Boost callbacks/source; do not hide existing hooks | +| D4 acceptance | A11 joint→declared ordered updates; at most6 alphas .1*.5^j; finite strict decrease; no residue on rejection, next reads accepted | NGBoost loop, outer LightGBM/XGBoost learners, Py-Boost callbacks; include existing line search | +| D5 scheduling | Shared prepared, heterogeneous K=1/2, independent budget/stop/RNG, reorder invariant, isolated failures retained | Independent loops+safe reuse, Py-Boost GPU loop; no assumed fusion requirement; record prepare/execute/recovery | + +Minimum counterexamples: + +- D1: positive/negative/zero residuals, weight0; nonsmooth points do not use ordinary second differences; + tau=.5 reduces to symmetric squared loss. +- D2: six fixed-bin rows, alternating A/B cohorts, g=[-6,1,1,1,1,2], h=1. Highest total gain can violate + cohorts. Enumerate best feasible candidate, then all-A-left/all-B-right data for no feasible split. + Cohort is not a feature; do not leak it to evade the constraint. +- D3: enumerate breakpoints/interior stationary points for unique optimum; subgradient contains 0; + increasing lambda moves toward anchor. Ordinary weighted quantile is not the penalized solution. +- D4: descent, reverse-direction full rejection, NaN, next parameter reads new state only after success; + verify tree/coefficient/raw/best/RNG semantics, not final loss alone. +- D5: reordering, retry, different stopping, same seed/different IDs, changed data identity. + Same shapes or skipped failures do not pass. + +H1/H2 contents stay outside interface-design material. F0.3 evaluation-side freezes cards/verifiers/ +hashes; F1 designers see D1–D5 only. Two empty IDs do not make E5 ready. If held-outs guide redesign, +reclassify as development, replace them and record the change. + +## 5. Minimal interface sketch and C1–C7 mapping + +Names remain sketches chosen for the actual callers above. + +```python +prepared = prepare(training_rows, feature_schema, binning, device=device) +problem = bind(prepared, target=target, weight=weight, offset=offset, + query=query, structure_input=structure_input) +with runtime.run(run_id=run_id, seed=seed, device=device) as run: + accepted = initialize(problem) + for step in range(rounds): + geometry = objective.geometry(problem, accepted) + direction = direction_rule(geometry) + # grow is ordinary Python: rewrite the composition or replace one function. + tree = grow(prepared, direction, aggregate=aggregate, + candidates=candidates, score=score, feasible=feasible, + partition=partition, leaf_solver=leaf_solver, + policy=growth_policy, run=run) + proposal = propose(accepted, tree, output_mapping=output_mapping) + accepted = accept_or_reject(problem, accepted, proposal, run) + model = export_model(accepted, output_schema=output_schema) +``` + +Expose aggregate→candidate statistics→feasibility/score→choose→partition→leaf solve. Candidates +contain thresholds/category sets/missing routes. Leaves get read-only indices/residuals/stats, +not G/H only. Algorithms may own loops; no universal trainer or registry-per-function requirement. + +| Capability | Actual callers | Delivery/acceptance | +|---|---|---| +| C1 typed data/targets | A2 categories, A4 queries, A6 vectors, A7 offset, A10 intervals, A12 structure | F0.2 data, F1.1/F1.5, all identity/shape/invalid inputs | +| C2 composable trees | A1 three policies, A4 row reduction, D2 extra stats | F0.2 tree, F1.2, exhaustive splits/routes/ties/feasibility | +| C3 learners/leaves/outputs | A5/D3 residuals, A6 vectors, A11/A12 mapping | F0.2 quantile/vector, F1.2/F1.6; K scalar runs are not vector leaves | +| C4 state/runtime | A11/D4 rejection/order, A13/D5 isolation | F0.2 state/runs, F1.1, F3 visible devices/transfers/sync | +| C5 artifacts | A2 mappings, A7 offset, A9 dual models, A10 spaces, A12 formula | F1 roundtrips, F5 wheels; standard raw inference without training plugins | +| C6 eval/authoring | All A/D, H1/H2 separately frozen | F0.3 runner/judge, F2 E5, F4 E3; bad artifacts fail | +| C7 workflows | Every A install→baseline→change→verify→save/infer | F2 trials, F5 E6; missing any workflow remains incomplete | + +Public raw is[N, K]; backends may use other layouts with recorded conversions. Variable topology +and scalar/vector payload replace fixed511/depth8 restrictions. Explicit proposal/accepted ownership +permits transactions/deltas without full copies each time. + +## 6. Historical implementation audit + +This audits code before Sprint 002 retirement; links pin50acfc6. Historical capabilities are not +current-namespace functionality. + +- [Standard model](https://github.com/jxucoder/openboost/blob/50acfc6/src/openboost/_models/_boosting.py) + applies CPU sample_weight; standard CUDA rejects supplied weights. Multiclass lacks the same + weight entry and fits per K. [Old multiclass docs](../docs/user-guide/models/multiclass.md) + do not prove v1 weighted/mapping support. +- [FormulaObjective](https://github.com/jxucoder/openboost/blob/50acfc6/src/openboost/_objectives.py) + already computes coupled GGN before per-parameter fitting;[model](https://github.com/jxucoder/openboost/blob/50acfc6/src/openboost/_models/_formula.py) + accepts separate model_input. [Tests](../tests/test_formula.py) are mainly synthetic. Do not + call old coupling new or full GGN automatic identifiability. A12 now has a real task definition. +- [ConfigBatch](https://github.com/jxucoder/openboost/blob/50acfc6/src/openboost/_batch.py)/ + [tests](../tests/test_batch.py) already recompute loss per configuration across rounds; independent + stop/error/RNG and compatible batching need new evidence. +- [Survival](https://github.com/jxucoder/openboost/blob/50acfc6/src/openboost/_models/_survival.py)/ + [tests](../tests/test_survival.py) use Weibull event/time. A10 log-normal is a new independent + recipe; old class names do not prove new noise/censor semantics. +- [Experimental Booster](https://github.com/jxucoder/openboost/blob/50acfc6/src/openboost/experimental/_booster.py) + limits CUDA eval/callback/early stopping. Validate v1 per case without inheriting limitations or + compatibility obligations; preserve mathematical failures. + +## 7. Sources and F0.1 boundary + +Task mathematics is explicitly defined here and independently derived/verified in F0.2. Sources +check interfaces/devices/data, not replace installed F0.3 capability smoke. + +- XGBoost [parameters](https://xgboost.readthedocs.io/en/stable/parameter.html), + [advanced custom objectives](https://xgboost.readthedocs.io/en/stable/tutorials/advanced_custom_obj.html), + [GPU](https://xgboost.readthedocs.io/en/stable/gpu/index.html): task/geometry/device boundaries. +- LightGBM [parameters](https://lightgbm.readthedocs.io/en/stable/Parameters.html), + [Booster](https://lightgbm.readthedocs.io/en/stable/pythonapi/lightgbm.Booster.html): objectives, rounds, leaves. +- CatBoost [regression](https://catboost.ai/docs/en/concepts/loss-functions-regression), + [ranking](https://catboost.ai/docs/en/concepts/loss-functions-ranking), + [multioutput](https://catboost.ai/docs/en/concepts/loss-functions-multiregression): targets/weights/devices. +- [NGBoost development](https://stanfordmlgroup.github.io/ngboost/5-dev.html), + [Py-Boost](https://github.com/sb-ai-lab/Py-Boost): geometry/scores/Python GPU controls. +- A1–A9 sources:[application matrix](foundation-application-contracts.md), + [insurance processing reference](https://scikit-learn.org/stable/auto_examples/linear_model/plot_tweedie_regression_insurance_claims.html). +- A10 [official loader/fields](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.datasets.load_veterans_lung_cancer.html), + [evaluation](https://scikit-survival.readthedocs.io/en/stable/user_guide/evaluating-survival-models.html). +- A12 [UCI Concrete, data/units/CC BY4.0](https://archive.ics.uci.edu/dataset/165/concrete+compressive+strength). + Actual hashes/group counts still require downloads; the formula is not supplied by that page. + +**F0.1 acceptance:**all A1–A13 inputs/outputs, data/splits, algorithms, oracles/controls; full R/C mapping, +falsifiable D1–D5, explicit devices/sketches. **References delivered:**F0.2 +[exit audit](../v1-sprints/f0-2-acceptance-ledger.md). **Still incomplete:**F0.3 downloads/hashes/ +capabilities/budgets/held-outs/judge. This file does not pass F0 or any E-gate. Freeze comparison +protocol next; no sole production oracle or premature trainer/kernel work. diff --git a/planning/gpu-python-foundation-design.md b/planning/gpu-python-foundation-design.md new file mode 100644 index 0000000..96b6eab --- /dev/null +++ b/planning/gpu-python-foundation-design.md @@ -0,0 +1,473 @@ +# GPU Python boosting foundation: design draft + +> Historical design and evidence record. Follow the [new F0–F5 plan](agent-boosting-foundation-plan.md) +> for subsequent work: the foundation is the product and incompatible redesign is permitted. +> Later user instructions supersede this document's requirements to reuse the old trainer, +> preserve fixed interfaces, and prioritize a distributional product. Original tests and failures remain evidence. + +Status: P0–P6 technical and installation verification complete; P7 quality passed, +GPU performance budget failed, and isolated profiling and design review completed. +Exact peak device memory and CUDA traces remain unverified. External adoption is +unverified. Date: 2026-09-05. + +This document defines interfaces, boundaries, verification, and execution order for +implementation by a medium model. Consult the execution checklist for actual +behavior and evidence from completed phases. The P2 baseline does not verify the +new extension API's GPU path. See the [execution checklist](gpu-python-foundation-execution.md). + +## 1. Product hypothesis to test + +> A Python researcher can change boosting objectives, tree construction, or update +> rules in an independent package, reusing OpenBoost's CPU reference, GPU data path, +> prediction, and verification tools without maintaining a fork. + +This research infrastructure direction is a hypothesis. Python implementation +percentage, GPU labels, and API counts are not success metrics. The existing +[product mission and evidence rules](../AGENTS.md) remain applicable; NaturalBoost +is the first real consumer. This iteration does not rename or promote the whole +repository as a general replacement. + +Bound the initial investment to a suggested 6–8 week exploration window with +technical gates. This limits scope; it is not a delivery promise. Establish one +trustworthy, modifiable path before expanding tree structures or algorithms. + +| Goal | Evidence available in this iteration | Conclusions it cannot establish | +|---|---|---| +| Impact | Three extension types change training results, checked against independent mathematical/routing references | Algorithmic novelty or publication impact | +| Adoption | Two independent wheels depend only on public experimental interfaces and run in clean environments | Self-authored examples do not prove external adoption | +| Value | Record implementation effort, end-to-end time, quality, device memory, and migration obstacles | Willingness to pay without interviews | + +The product gate for further investment is at least two external developers +trying extensions, with one completing their own method without core changes. +Record assistance and failures. Contact and invitations require separate user +authorization; prepare runnable materials without sending messages. Technical +acceptance does not substitute for this product gate. + +## 2. Starting point: integrate existing work first + +Design branch: `codex/gpu-python-foundation-design`, created from local `main` at +`82cf1e25b21a69093e85a270af7eb93c9ae7aa19`. + +After fetching on 2026-09-05, the remote baseline was fixed at +`6ebe3a8ced0e621b17e3cf63e31721af58471053`. The common ancestor was +`504fdd0bfc60e5d8e7518250e087fb7e4766d1b4`; there were 12 local-only and 9 +remote-only commits at branch creation, excluding later design commits. + +The remote already has `_trainer.py`, `_objectives.py`, FormulaBoost, and +WeibullAFT; reuse them. Preserve local categorical persistence/cardinality fixes, +batch fail-fast behavior, ScoringBench, performance CI, and AGENTS/learnings. +Do not reset away either side or write another training loop. + +Read-only `git merge-tree` found textual conflicts in `CLAUDE.md`, +`docs/getting-started/gpu-setup.md`, and `docs/getting-started/installation.md`. +This is not a complete semantic conflict list. Even a successful automatic merge +requires persistence, CUDA eligibility, and documentation evidence review. +Perform the merge in P0. + +| Inspected call path | Design consequence | +|---|---| +| Remote `fit_boosting` invokes the objective by channel but directly selects native trees / `fit_tree` | Add builder and schedule arguments here; explicit extensions must participate in dispatch | +| Remote `TrainerConfig` has no seed; growth uses global `numpy.random` calls | Pass seed/RNG through the selected path, not merely a new configuration field | +| Extensible primitives download histograms / sample node IDs; `compute_leaf_values_gpu` delegates to CPU | Add device batch representations and leaf reduction; renaming interfaces does not establish GPU residency | +| Native builder can update raw in place; old tree facade uses host arrays for persistence | Trainer owns raw updates; allow compact structure downloads after tree completion | +| Remote selects device kernels by distribution class name and broadly catches exceptions before CPU fallback | Declare capabilities, strict mode, and visible fallback; prohibit name-based builtin selection | +| Remote decides `unit_hessian` separately from `sample_weight` | Native `const_hess=1` may override nonuniform weights; reproduce before fixing | +| Eval/callbacks currently download host raw or predictions | Report their capability and cost separately; do not claim residency for every training configuration | + +These are source observations. The suspected weighted-Hessian issue has not yet +been reproduced on GPU. The local extension baseline is **35 passed, 1 skipped**, +which does not establish correctness of those GPU paths. Remote design speed +numbers are not verified evidence for this design. + +## 3. MVP support boundary + +Add interfaces under `openboost.experimental`, initially promising contracts only +within one explicit version. Preserve existing model defaults; connect NaturalBoost +and cover it with regression tests. Do not migrate every model immediately. + +| Item | MVP decision | +|---|---| +| GPU | One NVIDIA GPU, CUDA 12, Numba/CuPy RawKernel kernels; extensions receive CuPy arrays | +| CPU | NumPy oracle and runnable experimental engine; no GPU bitwise equality requirement | +| Data | Dense numeric features; sample-major input, feature-major bins; one-dimensional y, multichannel raw | +| Trees | Level-wise, scalar leaves, one tree per channel per round; maximum depth 8 | +| Objectives | Multichannel objectives with explicit CPU/CUDA implementations; two-parameter Normal is first acceptance case | +| Weights | Finite nonnegative sample weights with positive total; applied once, including zero weights | +| Sampling | Initial experimental GPU requires subsample=colsample=1; reject other values explicitly | +| Regularization | Initial experimental GPU uses L2, reg_alpha=0; enforce min_child_weight and min_gain | +| Missing/categories | Initial experimental builder rejects them; continue testing old model support and preserve category cardinality fixes | +| Metadata | Initial experimental fit rejects exposure/censoring; old models retain their existing capabilities | +| Callbacks/eval | CPU may use the existing flow; strict GPU residency initially covers training without callbacks/eval; report hybrid evaluation paths explicitly | +| Prediction/storage | CPU raw prediction required; verify GPU prediction and GPU save followed by CPU load | +| Excluded | Ray, multi-GPU, out-of-core, GOSS, train-many, vector leaves, autodiff framework interoperability, arbitrary dynamic training graphs | + +“Pure Python” means users and maintainers modify algorithms with Python/CuPy/Numba. +It does not promise no compilation, no CUDA runtime, or automatic GPU compilation +of arbitrary Python functions. + +## 4. Interfaces and training semantics + +### 4.1 One loop, three explicit extension points + +```text +Existing facades such as NaturalBoost experimental.Booster + \ / + fit_boosting + | + Objective.step(raw at round start) + | + TreeBuilder.build per channel + | + trainer applies StepSchedule coefficients + | + tree storage / eval / persistence / report +``` + +The following is a target contract, not an executable current API. Freeze names +in P3. Pass objects directly without a plugin discovery system. Thin adapters +connect existing Objectives without rewriting distribution mathematics. + +```python +class Objective: + channel_names: tuple[str, ...] + supported_devices: frozenset[str] # e.g. {"cpu", "cuda"} + + def init_raw(self, y, sample_weight=None, extra=None): ... + # CPU initialization once; returns {channel: finite scalar}. + + def step(self, raw, y, sample_weight=None, extra=None, *, context): ... + # Returns {channel: (grad, hess)} on the same device as raw. + + def loss_value(self, raw, y, sample_weight=None, extra=None, *, context): ... + def constrain(self, raw, extra=None): ... + +class TreeBuilder: + supported_devices: frozenset[str] + + def build(self, binned, grad, hess, *, config, context): ... + # Returns BuiltTree(tree, train_prediction=None). + +class StepSchedule: + def coefficients(self, round_idx, channel_names, base_learning_rate): ... + # Returns {channel: finite nonnegative scalar}; full coefficient, not multiplier. +``` + +`experimental.Booster(objective=..., tree_builder=..., step_schedule=..., config=..., +device="cpu"|"cuda", fallback="error"|"warn")` is a thin facade over the existing +trainer. It offers +`fit(X,y,sample_weight=None,eval_sets=None,callbacks=None,early_stopping_rounds=None)`, +`predict_raw(X)`, `save(path)`, and `load(path)`. Initially CPU supports evaluation +arguments; strict GPU rejects them before the first update. Do not duplicate the +distribution prediction API. Users can call +`objective.constrain(booster.predict_raw(X))`. + +Add `random_state` and the missing `min_gain` to `TrainerConfig`, keeping one source +for each hyperparameter. `ExecutionContext` supplies `device`, `xp` (NumPy/CuPy), +fit-scoped `rng`, `round_idx`, and `channel`. New interface objects do not infer +execution location from a global backend. Internal legacy adapters still use +`backend_context`, restoring it after fit; mixed-backend concurrent fits in one +process are prohibited. Objective context.channel is None; builder context.channel +is the current channel. Plugins do not mutate context. Builtin adapters consume +the added context argument before invoking existing objective mathematics. + +### 4.2 Explicit mathematical and array contracts + +1. Each raw/gradient/Hessian channel is contiguous float32 `(n_samples,)`. + Keys must be complete with no extras and fixed channel order. Reject y that + is not one-dimensional rather than hiding errors with ravel. +2. `grad` points toward increasing loss. Default leaf value is + `-sum(grad)/(sum(hess)+reg_lambda)`. `hess` is nonnegative effective curvature + for tree optimization, possibly Fisher/preconditioning rather than the exact + Hessian. Regularization is finite and nonnegative. A zero denominator with + G=0 yields a zero leaf; a zero denominator with nonzero G is an error. +3. The objective applies sample weights to both grad and hess **once**; trainer + and builder do not reapply them. Default loss is a weighted mean. Reject all-zero, + negative, NaN, or infinite weights. +4. Initially disable constant-Hessian hints for custom objectives. Builtin hints + require a suitable objective, no sample weights, and no sampling/transformation + that changes Hessians, with comparison tests; `natural=True` alone is insufficient. +5. `step` reads all raw channels at round start once and returns all channel + statistics. Build trees in fixed order without recomputing other channel + gradients after each update. Inputs are read-only; returned buffers live at + least until the round ends. +6. `F[r+1,k] = F[r,k] + eta[r,k] * tree[r,k](X)`. Builders cannot modify raw. + `BuiltTree` contains a tree and optional training predictions, with no implicit + “raw already updated” state. The trainer updates exactly once and stores the + actual `eta[r,k]`. +7. Initial `StepSchedule` only supplies predetermined per-round/channel coefficients. + It cannot read/modify raw or the old tree collection. Line search, momentum, and + reweighting all old trees are outside this deliberately narrower-than-UpdateRule contract. +8. CUDA extensions receive CuPy arrays; internal Numba uses zero-copy CUDA Array + Interface views. MVP uses the default stream; caller-provided streams are + unsupported. Test owner references and lifetime; do not mistake host ndarrays + for device arrays. Initialization/final output may copy; no implicit `.get()` + in the hot path. +9. CPU seeds reproduce results without changing global RNG state. Old sampling + paths receive the same scoped Generator; explicit indices may be reused in + CPU/CUDA comparisons. No GPU bitwise determinism claim. + +### 4.3 Dispatch, errors, and reports + +Explicit builders take precedence even when inputs qualify for a native path. +Default builders may select verified native kernels; adapters pass `pred_gpu=None` +and the trainer applies updates. Some fusion may initially be lost: measure its +cost without breaking update semantics for a speed number. Do not yet rewrite +all of the separate old `_models/_boosting.py` loop. + +CUDA capability checks use explicit declarations and builtin implementation +identity, not `type(...).__name__`. Experimental GPU defaults to `fallback="error"`: +unsupported capabilities fail before the first training update. `fallback="warn"` +may select and report a complete CPU path before fit only for known capability +gaps. Do not catch arbitrary round-time exceptions and silently continue after +copying raw. Numerical errors, invalid shapes, and kernel failures retain their +causes and fail. Existing facades may retain legitimate hybrid execution if +reports describe it clearly. + +`fit_report_` records at least requested/actual device, actual objective/tree/update/eval +locations, builder path, fallback reason, seed, trees per channel, and timing +synchronization boundaries. Transfer counts cover only OpenBoost wrappers; +external CuPy code may copy independently, so counts are not process-wide proof. +Check residency with a small profiler trace as well; shapes or `cuda.is_available()` +are not substitutes. + +## 5. Tree primitives: Python modification with a GPU data path + +Keep the host `dict[int, NodeHistogram]` API compatible and add an experimental +batch API without pretending return-type compatibility. Reuse histogram/split/partition +kernels first, establishing boundaries before performance optimization. + +| Batch representation/operation | Target shape and responsibility | +|---|---| +| HistogramBatch | grad/hess `(node_slots, features, 256)` float32; int32 sample counts and bool active mask on the same device | +| SplitBatch | Per-node_slot feature/threshold/child IDs/gain/valid-mask arrays on the same device | +| `build_histograms` | Aggregate actual sample node IDs; define zero weights, empty nodes, and inactive slots | +| `find_splits` | Compare candidates in batches, exclude invalid children, break equal-gain ties by feature then threshold | +| `partition` | Update sample node IDs using splits; return a new array or explicitly declare in-place buffers | +| `leaf_values` | Device sum/reduction and leaf rule; no grad/hess/sample-ID download | + +To bound dynamic allocation, initial level-wise growth uses fixed complete-binary-tree +slots with a device active mask: root=0, left=2i+1, right=2i+2; leaf children=-1. +Fixed per-level loops need not download sample arrays to count active nodes. +Maximum depth is 8; default histogram temporary budget is 256 MiB. Reject budget +excess rather than implementing out-of-core. Bin 255 remains reserved for missing +values even on a path that rejects missing inputs. Independently test constant +features, all-zero effective curvature, no valid split, and negative/nonfinite gain. + +`LevelWiseBuilder(leaf_rule=...)` exposes the first minimal tree extension: a leaf +rule receives batched G/H, config, and context and returns same-device leaves. +Users needing deeper changes can compose public batch primitives. Arbitrary +Python callbacks are not automatically injected into compiled kernels. + +Return the existing scalar `TreeStructure`. Allow O(tree_nodes) structure/leaf +downloads after each tree, retaining device caches for training predictions. +This reuses CPU prediction and serialization without rewriting all tree objects. +Allowed transfers are initialization, compact structures per tree, necessary +scalar state/error checks, explicit evaluation, and final output. The hot path +must not download O(samples) raw/grad/hess/node IDs or complete histograms. + +Split-gain scaling and min_gain comparisons follow verified CPU behavior, with +independent formula tests in P3. If CPU/native formulas disagree, fix the +correctness issue instead of temporarily relaxing parity thresholds. + +## 6. Three extension experiments, two independent packages + +Place packages in `examples/extensions/normal_fisher/` and +`examples/extensions/bounded_leaves/`, each with pyproject, README, and independent +tests. Build and install the OpenBoost and extension wheels in fresh environments, +then run outside the repository. Prohibit editable/PYTHONPATH injection, private +imports, core copies, or dispatch monkeypatching. Sources may share a repository, +but tests must verify the installation boundary; this is not external adoption. + +**A. External two-parameter Gaussian/Fisher objective.** Raw channels are mu and +log_sigma; `s2=exp(2*log_sigma)`, `g_mu=(mu-y)/s2`, `h_mu=1/s2`, +`g_log_sigma=1-(mu-y)^2/s2`, and `h_log_sigma=2`, then apply weights. Initialize +location and variance using weighted means, with variance floor 1e-6. Minimal +tests use a finite range and reject nonfinite state. Check gradients against +independent finite-difference NLL and Fisher against an analytic reference. +Implement CPU/CUDA. This tests independent implementation and extensibility, +not Gaussian Fisher novelty. + +**B. External bounded-Newton leaf.** During tree growth use +`clip(-G/(H+lambda), -c, c)`, with zero leaves for no effective samples and finite +c>0. Compare against an unclipped reference and observe changes in actual leaves +and next-round gradients, not just callback invocation. GPU clipping/reduction +runs on device and saved results persist. The method retains the default split +criterion; it does not reoptimize every split for a clipped objective. + +**C. Nonconstant per-channel schedule.** Implement in package A: +`eta[r,k] = base_lr * channel_scale[k] / (1 + r/tau)`, tau>0, mu scale=1 and +log_sigma scale=0.5. Hand-check all coefficients in a two-round example, then +check training raw, new predictions, early-stop restoration, and save/load. +This verifies the update interface, not line search or a new training algorithm. + +After A/B, arrange an external author experiment. If an actual external method +still needs private imports or a fork, record the missing interface before +adding speculative hooks. + +## 7. Persistence and compatibility + +Save the binner, base scores, channels, tree arrays, actual per-tree coefficients, +and version. Coefficients must share semantics across prediction, eval, +early-stop truncation/restoration, and persistence, not just fit. For old files +without coefficient state, synthesize it from old learning_rate and test. + +Experimental Booster raw inference does not depend on custom objective/builder/schedule +code. Do not serialize lambdas or arbitrary training objects for deployment. +Loaded raw predictions work; further training requires resupplying the objective. +Initial warm-start/resume is explicitly unsupported. Existing builtin model +prediction transforms retain their behavior. + +If the shared serializer changes, increment its actual current version and keep +old-version rejection rules. Regress any touched numeric, missing, categorical, +symmetric/linear/vector, or other specialized state; do not delete unrecognized +fields for the experimental path. + +## 8. Correctness, performance, and product gates + +| Gate | Required evidence | +|---|---| +| G0 Integration | Both histories preserved; local fixes, remote new models, and CPU suite pass or existing failures are explicit | +| G1 CPU contract | Independent math oracle, seed, single weighting/update, explicit dispatch, error paths, saved prediction equality | +| G2 CUDA baseline | Real GPU weighted/unweighted Normal/Poisson end-to-end checks; no known failures carried into the new API | +| G3 Resident extensions | A/B independent wheels run on CPU/CUDA and C changes updates; verify gradients, splits, leaves, predictions, and task metrics | +| G4 Engineering value | Cold/warm end-to-end time, device memory, transfers, and matched quality, with committed raw results | +| G5 External adoption | External authors' extensions and obstacles; no attempts or all requiring forks does not pass | + +Prefer exactly representable micro-oracles without approximate ties. Starting +hist/grad/leaf tolerances: rtol=1e-5, atol=1e-6; split topology must match on these +data. Test exact optimal ties separately. For larger floating reductions use +explained prediction/quality bounds, not mandatory equality of every tree. + +Freeze seeds=0,1,2 in advance. Start numeric real regression with a frozen +sklearn California Housing version/hash. Record download failures; do not replace +real evidence with synthetic data. Fit preprocessing only on training partitions. +Compare held-out Normal NLL, CRPS, and interval coverage; use a known Poisson +generating process for count checks. Separate synthetic scaling and real-data +quality from each other and from official ScoringBench. + +Freeze P2 before observing implementation results. Prespecified screening bounds +for the same algorithm/configuration: absolute mean NLL difference no more than +`0.01*max(1,abs(baseline_NLL))`, CRPS relative degradation no more than 1%, and +90% interval coverage difference no more than 1 percentage point. Report every +seed and investigate failures. Three seeds do not establish statistical significance. +These wider real-data bounds cannot waive micro-oracle correctness. + +A default GPU median end-to-end fit regression above 20% versus the integrated +old path triggers profiling and design review, without changing quality or +hiding warmup. This is a design budget, not measured performance or a speed +promise for custom algorithms. Prioritize reproducible records of public +interfaces, additional method code, and GPU cost needed to implement a method. + +## 9. Modal verification design + +P1 adjustment: use a separate `benchmarks/foundation/modal_app.py`, preserving +the old runner. The old app registers source mounts and other jobs with loose +dependencies, which would compromise wheel isolation. It copies a single test +file, uses loose dependencies, and some entry points only print failures; it +cannot directly supply this iteration's evidence. Modal supports image-time +dependency installation and explicit local-file inclusion, with official test +examples. Use the current SDK's uv installation path, not ad-hoc pip installs. +Sources: [image guide](https://modal.com/docs/guide/images), +[CI example](https://modal.com/docs/examples/ci-on-modal). + +- Python 3.12, CUDA 12, fixed image digest and Linux dependency lock including + numba-cuda, CuPy, pytest/xdist. Validate the lock in Linux; a macOS installed-package + list is not a Linux environment. +- Build a wheel from a clean implementation commit and calculate SHA256. Upload + only the wheel, required tests/conftest/config, extension wheels, and manifest. + Verify site-packages import provenance and wheel hashes. Do not upload the + whole workspace, .git, credentials, or user data. +- Default one T4, concurrency 1. Remote timeouts: `foundation_smoke`=300 seconds, + `foundation_correctness`=1800, `foundation_benchmark`=1800; leave 60 seconds per + pytest subprocess for collection. Run smoke then correctness before benchmarks; + do not automatically expand GPU models or matrix size. +- App retry=0. Record cumulative remote seconds, failures, and existing run IDs. + Initial plan: at most approximately 1 GPU-hour. This is not a hard spending cap; + record image builds/startup and platform restarts separately. Modal timeouts + limit individual executions and infrastructure retries may occur. + [Timeouts](https://modal.com/docs/guide/timeouts), + [failure handling](https://modal.com/docs/guide/functions). +- Local entry points such as `::foundation_smoke` control status. Any pytest + failure, missing required GPU test, skipped selected required test, environment + validation failure, or unretrievable result makes the local command fail. + Legitimate CPU skips in mixed files are not failures; list required node IDs. +- Every result includes run ID, source SHA/dirty, wheel hash, dataset/version/hash/split, + exact commands, dependencies, CPU/RAM/threads, GPU/driver/runtime, actual CUDA + path, fallback, synchronization/warmup, JSON, JUnit, and logs. Save locally in + `benchmarks/results/foundation//`; explicitly adjust ignore rules to + commit reviewed frozen results. +- Correctness jobs do not advertise speed. Benchmarks measure complete fit/predict, + first compilation separately from warm medians, quality/peak device memory, + and differing CPU/GPU resources. Use at least 3 warm runs and synchronize GPU + timing boundaries. + +At planning time, Modal credentials, image builds, GPU quota, and actual prices +were unverified. Start with a minimal smoke; preserve failures and fix their +specific causes rather than repeatedly retrying long jobs. + +## 10. Conditions for changing direction + +- If both independent extensions still need core changes, the interface hypothesis + failed: narrow or repair the contract first. +- If extensible GPU execution consistently lacks economic benefit on target + workloads, preserve CPU research utility and pause GPU platform expansion. +- If external authors only need custom distributions, return investment to the + distributional product instead of expanding the core for generality. +- If authors repeatedly contribute different algorithms and want independent + packages to depend on OpenBoost, reconsider separate split/leaf gradients, + vector leaves, and deeper update interfaces, each motivated by an actual algorithm. + +See the [impact/adoption/value study](../learnings/2026-09-05-impact-adoption-value-strategy.md) +for competition and broader strategy. This plan seeks a narrow foundation that +can be adopted or falsified; it does not claim an existing ecosystem. + +## 2026-09-05 goal review after P4.1 + +The larger objective remains useful, trustworthy distributional/risk modeling +and a shorter path from a research idea to a usable implementation. The GPU +foundation is one bounded hypothesis supporting that objective. Passing kernels, +more APIs and more Python code are not adoption or value evidence. + +G0/G1/G2 and one histogram primitive have evidence. G3 (independent GPU +extensions), G4 (matched-quality end-to-end cost) and G5 (external author use) +remain open. Continue the smallest path through numeric split/routing, one +bounded leaf rule and two-channel Normal training to the two independent wheels. +Do not add tree families, custom split criteria or extra device support on the +way. The next product checkpoint is a reproducible method implemented through +public APIs, with implementation effort, installation obstacles and runtime +cost recorded; then an external author task, not another list of kernels. + +Keep the existing stop conditions: if authors only need custom distributions, +return investment to the distributional product; if GPU gives no end-to-end +benefit, keep it optional. Prepare author materials without sending invitations +or publishing. External attempts and retention still require actual users. + +Implementation clarifications: P4.1 uses a small CuPy RawKernel for its separate +layout/count contract; Python percentage and Numba-only kernels are not goals. +Its non-default stream test covers that primitive only, not the future whole +trainer. P4.2 numeric splitting requires positive curvature in both children, +including when min_child_weight=0; this avoids inventing information about empty +versus zero-weight bins from G/H alone. Zero-curvature nodes stay leaves; missing +and categorical builder support remains out of scope. These limits must remain +visible in the public contract and independently tested. + +Sequencing adjustment from this review: after the minimal P4.4 builder works, +run P6's independent CPU wheel examples before completing P5's strict GPU +integration. Record public/private imports, installation failures, method code +and steps to correct output. Fix demonstrated API obstacles first. GPU parity +and independent GPU wheels remain mandatory afterward; no external adoption +is claimed from examples we write ourselves. + +## P7 observed value boundary + +The [committed resident matrix](../benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md) +passes quality but shows 13.899x default CUDA fit time versus the paired legacy +CUDA path. The 20% regression budget failed. P6/G3 proves independent installed +extensions; it does not justify replacing the legacy path. G4 retains negative +performance evidence and explicit profiler/peak-memory limits. G5 is still open. +The product mission remains calibration-first distributional risk, with this +API available for bounded research rather than a claimed general GPU speed layer. + +The [isolated follow-up](../benchmarks/results/foundation/20260905T184856Z-dcd49569/README.md) +places most diagnostic wall time in tree construction and its extension boundary, +not objective math. The original uninstrumented timing verdict remains unchanged. diff --git a/planning/gpu-python-foundation-execution.md b/planning/gpu-python-foundation-execution.md new file mode 100644 index 0000000..d5207ec --- /dev/null +++ b/planning/gpu-python-foundation-execution.md @@ -0,0 +1,512 @@ +# GPU Python foundation: medium execution checklist + +> Historical P0–P7 execution and evidence record, no longer the active queue. +> Continue from [F0 in the new plan](agent-boosting-foundation-plan.md). The user +> clarified that the foundation is the product and permits incompatible redesign; +> this checklist's old interface and trainer-reuse requirements no longer constrain work. + +Status: P0–P6 complete. P7.1 real-data quality passed; performance budget failed +(initially 13.899x, then 12.888x after fixed-slot optimization). P7.2 developer +materials complete. P7.3 diagnostics without concurrent sampling and design review +complete. Exact peak device memory and CUDA traces remain unverified. G5 external +adoption incomplete. See the [design contract](gpu-python-foundation-design.md). +P1 results: 12 local result-protocol tests passed; real single-T4 smoke 2 passed / +0 skipped, with wheel provenance and device invocation verified. +[P1 learning and raw results](../learnings/2026-09-05-foundation-p1-modal.md). +P0 results: CPU regression 749 passed / 34 skipped, focused loader regression +21 passed, lint/docs/packaging passed. See the +[P0 learning](../learnings/2026-09-05-foundation-p0-integration.md). +New modules, tests, and Modal entry points described below were plan targets, +not proof that they already existed when the plan was written. + +## How to use this checklist + +Work in dependency order on `codex/gpu-python-foundation-design`, one small task +at a time: read the call path → write the smallest failing test → implement → +verify → update learning → inspect staged diff → commit. Resolve routine choices +without requesting repeat approval for authorized work. The user authorized Modal +for this plan. Do not push, release, change main, or contact external authors. + +When evidence falsifies a design assumption, provide a minimal reproduction and +update the relevant design section; do not conceal the issue by expanding scope. +Distinguish implementation, CPU verification, real-GPU verification, and unverified +external adoption in reports. If execution resources are limited, finish one +verified commit and identify the next task without declaring the full route complete. + +## Planning snapshot + +| Item | State at planning time | +|---|---| +| Original branch | `main`, clean | +| Current branch | `codex/gpu-python-foundation-design` | +| Original HEAD | `82cf1e25b21a69093e85a270af7eb93c9ae7aa19` | +| Fetched remote | `6ebe3a8ced0e621b17e3cf63e31721af58471053` | +| Local/remote-only commits | 12 / 9, not yet merged; excludes later design commits | +| CPU extension baseline | `tests/test_extensibility.py`: 35 passed, 1 GPU skipped | +| Modal | User authorized; no job, credential verification, or billing initiated in the planning round | + +Recheck `git status --short --branch` before execution; the user may have edited +files. Merge the fixed SHA above for reproducibility. Review later remote commits +separately rather than unconditionally pulling the latest state. + +## P0: Integrate the remote unified trainer and local fixes + +**P0.1 — Freeze the baseline and resolve the merge.** + +- Read local AGENTS, relevant learnings, and remote `planning/unified-engine-design.md`. +- Verify the branch contains the original local HEAD and design files, with no + unexplained working-tree changes. +- Preserve both histories with merge, not rebase/reset: + +```bash +git merge --no-commit --no-ff 6ebe3a8ced0e621b17e3cf63e31721af58471053 +``` + +- Known conflicts: keep `CLAUDE.md` pointing to canonical AGENTS. Reconcile GPU + installation docs with remote new models and local limitations; do not restore + performance claims lacking raw evidence. +- Review `_persistence.py`, `_array.py`, `_core/_tree.py`, `_models/_boosting.py`, + CI, ScoringBench, and examples semantically; preserve categorical/save/load/batch guards. +- Run P0.2 before committing. Combine the two steps into one verified merge; + do not commit unresolved conflicts. + +**P0.2 — Integration regression.** + +Start with local fixes and remote new models: + +```bash +OPENBOOST_BACKEND=cpu uv run pytest tests/test_categorical.py tests/test_persistence.py tests/test_batch.py tests/test_extensibility.py tests/test_formula.py tests/test_survival.py -n 0 -q +``` + +Then run the CPU suite, production lint, documentation build, and package build: + +```bash +OPENBOOST_BACKEND=cpu uv run pytest tests/ -m "not gpu and not benchmark" --tb=short +uv run ruff check src/openboost/ +uv run mkdocs build +uv build +``` + +First ensure dependencies with `uv sync --locked --extra dev`. Record concrete +platform restrictions on optional dependencies without claiming full verification. +Separate pre-existing failures from merge regressions. Fix relevant correctness +failures first; retain minimal reproductions and scope for unrelated legacy +issues. Do not advertise the foundation as usable before G0. + +Acceptance: both parents are ancestors of new HEAD; AGENTS and all local fixes +remain; new models import and tests run. Suggested commit: +`merge: integrate unified trainer and local correctness fixes`. + +## P1: Establish a traceable Modal test path that propagates failures + +**P1.1 — Result protocol and test bundle.** Main files: separate +`benchmarks/foundation/` app to avoid old source mounts, new `tests/foundation/`, +required locks/test configuration, and `benchmarks/results/.gitignore`. + +- First write local tests requiring nonzero exit for remote failure, timeout, + missing reports, and skipped mandatory GPU tests. +- Manifest generation never reads or prints credentials; freeze source SHA, + dirty state, wheel/data hashes, and argv. +- Package required tests/conftest/config and lock Linux/Python 3.12/CUDA 12 + dependencies including CuPy and xdist. Install with uv; do not point benchmark + environments at workspace src. +- Explicitly allow only reviewed evidence under `benchmarks/results/foundation/` + into Git. Keep temporary output ignored; do not broadly force-add all logs. + +**P1.2 — Minimal real-GPU smoke.** New `::foundation_smoke`, one T4, 300 seconds, +retry=0. + +- Verify the actual CUDA device, values and owner lifetime of Numba/CuPy zero-copy + views, and installed-wheel provenance. +- Run a tiny builtin Normal fit/predict, returning environment, JUnit, and status; + draw no speed conclusion. +- Test both successful and failing entry-point results locally; a “GPU available” + boolean is insufficient. + +Planned command, available after P1 implementation: + +```bash +uv run modal run benchmarks/foundation/modal_app.py::foundation_smoke +``` + +Acceptance: locally retrieved real-GPU results match the source wheel and failures +propagate to CLI status. Record GPU time, commands, and limits. Fix specific +failures without adding GPU models or retrying long jobs. + +## P2: Fix correctness first and freeze the old-path baseline + +**P2.1 — Weighted constant-Hessian regression.** Main files: `_trainer.py`, +`_objectives.py`, and corresponding tests; source paths are relative to +`src/openboost/` and must follow the actual implementation. + +- First failing test: fixed binned X and raw/grad, with zero and nonuniform + positive sample_weight; compare weighted histograms, Newton leaves, and + one-round predictions between CPU and native CUDA. +- Follow with end-to-end weighted Normal/Poisson fits; a mock proving argument + forwarding is insufficient. +- Fix const_hess eligibility first: nonconstant Hessians must read the array. + Initially hints may be disabled even for uniform weights. Inspect initialization + and gradient weight semantics without changing every model's algorithm. +- If the static suspicion does not reproduce, explain why and add protection; + do not turn a suspected issue into a claimed fixed bug. + +**P2.2 — Capabilities, fallback, and seed.** + +- Failing tests: custom objects sharing builtin distribution names must not select + builtin CUDA mathematics; broad except must not swallow kernel RuntimeError; + unsupported-capability fallback must be visible. +- Add scoped RNG to relevant fits; test same-seed repeatability, different-seed + effects on actual sampling, and unchanged global NumPy RNG state. Reject + unsupported MVP GPU sampling and connect old CPU sampling to the seed. +- Unsupported parameters fail before the first update without leaving fitted + attributes that resemble a completed model. + +P2 acceptance results: real T4 14 passed / 0 skipped, 12 baseline configurations, +24 fits. All three seeds passed quality, fallback, callback/eval, and bidirectional +save/load checks. [Baseline and raw results](../benchmarks/results/foundation/20260905T084129Z-2574e387/README.md). + +**P2.3 — Freeze the integrated usable baseline.** + +- Run Normal/Poisson gradient → split → leaf → raw → metric through + `::foundation_correctness`. Separately verify CPU fallback, callback/eval + download boundaries, and CPU/GPU saved predictions. +- Freeze real-data hash, splits, and seeds. Separate training residency without + eval from end-to-end tasks with eval. +- Store default trainer/native correctness and cold/warm timing as the P7 baseline. + Fix an incorrect or quality-failing baseline first; bad baselines cannot justify + new-code acceptance. + +Acceptance: relevant G0/G2 correctness passes, clean baseline source SHA, committed +results. Initial collection may use a bounded benchmark entry point; avoid +repeating the full matrix unnecessarily. + +## P3: Freeze the minimal public contract on CPU + +Completed: CPU facade, strict objective, explicit builder, per-channel schedule, +plugin-independent raw-inference persistence, and coefficient-aware early stopping. +182 relevant tests passed; 3 old long-running GBDT tests were interrupted and +are not counted as passes. A clean `f414b8c` wheel reproduced predictions exactly +in an isolated Python 3.12 environment. +[Contract, installation limits, and reproduction](../learnings/2026-09-05-foundation-p3-cpu-contract.md). +P3 supports CPU only; the native extension adapter is deferred to P5. The default +CPU builder temporarily rejects `reg_lambda=0/min_child_weight=0`. G1 passed +within this explicit boundary. + +**P3.1 — Experimental facade and objective contract.** Main files: new +`src/openboost/experimental/__init__.py`, thin interface/type modules, `_trainer.py`. + +- Failing tests: an independently implemented two-channel objective runs through + the facade; invalid shape/device/keys, nonfinite values, invalid weights, + missing capabilities, and unsupported parameters fail; existing adapters pass. +- Do not duplicate the training loop. Booster config, context, BuiltTree, and + reports carry only the designed state. +- Freeze math conventions, float32/device/ownership, and a CPU oracle without near ties. +- Do not re-export all of `_core`; expose only types/functions actually used here. + +**P3.2 — Builder and schedule dispatch.** + +- Failing tests: explicitly supplied builders run even on native-eligible input. + All two-channel/two-round coefficients apply once; training raw agrees with + new predictions. +- Builders never receive writable raw references. Native adapters use + `pred_gpu=None`; the trainer applies updates. +- Contract tests catch users reusing a buffer and overwriting a previous channel; + do not conceal this with full host copies every round. +- Default schedules regress to old learning_rate. Custom coefficients must be + finite, nonnegative, and complete across channels. + +**P3.3 — Coefficient persistence and callbacks.** Main files: `_persistence.py`, +`_callbacks.py`, trainer/facade. + +- Failing tests: nonconstant per-channel schedules preserve fit raw / predict / + save-load equality; early-stop truncation and restore handle trees and coefficients. +- Saved experimental models support raw inference in a clean CPU environment + without extension packages; do not pickle training plugins. +- Old files without coefficients load using old learning_rate. The experimental + facade rejects old categorical state; retain the shared legacy loader's warnings. +- Round-trip every touched shared tree state, especially missing/categories/specialized leaves. + +Acceptance: experimental CPU contract implemented, no relevant default-model +regression, G1 passed. Commit each P3 step separately; do not defer prediction/ +storage semantics until GPU completion. + +## P4: Build the device path one primitive at a time + +Suggested new file: `src/openboost/_core/_batch_primitives.py`, plus existing +CPU/CUDA backends. Filenames may follow code organization; do not arbitrarily +expand responsibilities or interfaces. + +**P4.1 — Batch histogram / CPU oracle. Complete.** + +74 relevant CPU tests passed; clean `cf61611` wheel on real T4: 3 passed / 0 skipped. +Maximum G/H errors against an independent sample oracle: 9.835e-7 / 1.252e-6; +counts exact. [Evidence and limits](../benchmarks/results/foundation/20260905T150940Z-a5c80f7f/README.md). +This commit completed only histograms; split/routing verification follows in P4.2. + +- Use direct sample sums as an independent oracle, not the production histogram + function to generate expected values. +- Test weighted/zero-weight, empty nodes, inactive slots, constant features, + reserved missing bin, and memory budget. +- Keep GPU aggregation on device, bypassing legacy host dict wrappers. Counts + remain separate from H. + +**P4.2 — Split / routing. Complete.** + +`b75b95a`: 90 relevant CPU tests passed; real T4 4 passed / 0 skipped. Independent +oracles verified topology, exact ties, gain bounds, actual sample routing, and +next-level statistics. [Raw evidence and limits](../benchmarks/results/foundation/20260905T151819Z-e5eb30b7/README.md). +Numeric L2 with positive-curvature children; Booster GPU integration still awaited +P4.3/P4.4/P5. + +- Exhaustively enumerate valid splits on tiny matrices; check gain, + min_gain/min_child_weight, tie order, and no-valid-split behavior. +- Device-partitioned node IDs match the CPU oracle; next-level histograms use + actual routed rows. +- Never approximate children by scaling parent histograms. Use fixed complete + slots by default, explicitly masking empty slots. + +**P4.3 — Leaf reduction / rule. Complete.** + +`4da7f4b`: 107 relevant CPU tests passed; real T4 5 passed / 0 skipped. Per-sample +reduction references, weighted Newton/clipped leaves, next-round gradient changes, +and rule ownership passed. [Raw evidence and limits](../benchmarks/results/foundation/20260905T152639Z-62d96727/README.md). +CPU uses the real trainer with a root builder. GPU is a two-round primitive +composition at this point, not a complete Booster. + +- Real GPU sum/reduction; independently check nonuniform weights and zero-effective nodes. +- Public leaf rules return same-device arrays. Bounded leaves change both leaf + values and next-round gradients. +- Test no grad/hess/node-ID downloads or full host histograms. + +**P4.4 — LevelWiseBuilder assembly. Complete.** + +`e02403b`: 120 relevant CPU tests passed; real T4 6 passed / 0 skipped. +[Raw evidence](../benchmarks/results/foundation/20260905T163823Z-7b16b556/README.md) +covers independent whole-tree references, device cache lifetime, two rounds/two +channels at 16/4097 rows, and CPU-loaded predictions. This builder is explicitly +selectable; preserve the default CPU builder's existing parameter boundary. +GPU checks directly compose builders; full GPU Booster.fit still awaits P5. + +- Compose the primitives into a selectable experimental builder; download only + compact tree arrays after completion. +- Keep device prediction caches; default-stream/view lifetime tests include + prediction after fit and temporary-buffer release. +- Verify one tree, then two rounds/two channels on CPU/CUDA before scaling n. + Do not add vector leaves or leaf-wise growth in this step. + +Each P4 GPU task needs real-GPU parity before being marked passed. Reuse one +image and small selectors rather than running the full Modal suite for each edit. +Without GPU availability, finish CPU/test preparation but do not skip the device +gate and claim completion. + +### Move the usability check earlier + +Goal-review sequencing adjustment: after P4.4's minimal builder works, run the +**independent CPU-wheel** parts of P6.1/P6.2/P6.3 before strict P5 GPU integration. +GPU acceptance remains unchanged. Record nonpublic dependencies, method code +size, installation obstacles, steps to correct output, and plugin removal after +saving. Fix demonstrated core/interface obstacles before adding GPU features. +Self-authored CPU packages are not external adoption: G5 remains incomplete. +Then complete P5 and P6 GPU verification. + +## P5: Integrate strict GPU execution and reports + +**Core execution verified; profiler-trace gap retained.** `8c34b26` implementation, +`4299858` extension tests: real T4 7 passed / 0 skipped. Normal/Poisson ordinary/ +natural gradients, default/external builders, weighted two-channel schedules, +CPU loading, and error rollback passed. +[Raw evidence](../benchmarks/results/foundation/20260905T180000Z-7d73ba83/README.md). +Full CPU regression: 904 passed / 34 skipped; final focused tests: 89 passed. +nsys was unavailable, so there is no profiler trace. Report only named transfer +wrapper checks and device-copy cost. GPU eval/callbacks/early stopping are +explicitly rejected. Next: P6 independent GPU wheels. + +**P5.1 — Actual dispatch and residency.** + +- One trainer runs default/external builders; check all strict-GPU capabilities + before training. +- Keep objective/raw/y/weights and updates on device. Report tree finalization + and explicit eval/output copies. +- Verify no native bypass of explicit extensions, duplicate raw updates, or + duplicate sample weighting. +- Host-transfer wrapper tests cover internal library paths; add one small + profiler run for external CuPy/internal kernels. If unavailable, state the + gap rather than claiming proof of no transfers. + +**P5.2 — Regression and negative tests.** + +- Compare default numeric NaturalBoost CPU/CUDA; same-name custom objectives, + broken kernels, and wrong-device outputs fail appropriately. +- Reject unsupported categorical/missing/exposure/eval/sampling/regularization + or use the declared complete CPU fallback. Never silently honor only some + parameters. Precisely report each stage of retained legitimate legacy hybrids. +- GPU fit → save → CPU load prediction, nonconstant schedules, early-stop state, + and existing persistence regression. + +Acceptance: G3 core path passes and fit_report_ agrees with actual trace; fix +necessary compilation/lifetime issues. If default execution slows significantly, +preserve correctness and enter P7 profiling, without hiding fused updates as +builder side effects. + +## P6: Two real installation boundaries and reproducible examples + +**Independent CPU/GPU installation verified.** GPU `43fcda3`: real T4 3 passed / +0 skipped; 8 training combinations and standalone GPU examples passed. A new +process exactly reproduced 9 models after uninstalling both plugins. +[GPU evidence and initial startup failure](../benchmarks/results/foundation/20260905T181351Z-1aee9568/README.md). +Both packages at 0.2.0 declare CPU/CUDA, with no core changes. Maximum GPU raw +prediction error 3.58e-7. Updated independent CPU install: 7 passed; relevant +regression: 90 passed. Initial CPU verification follows. + +**Initial CPU verification.** `3f8addd`: two independent wheels use only public +APIs in a fresh venv outside the repo. Five independent math/composition tests +passed; a 64-row weighted public example ran. Six models reproduced exactly +after uninstalling both plugins and restarting Python. Relevant regression: +76 passed. [Full evidence and installation obstacles](../benchmarks/results/foundation/p6-cpu-3f8addd/README.md). +Method source: 89 + 22 lines, no core changes. Development Numba/llvmlite source +installation failed on Intel macOS; examples pin installable CPU dependencies. +This initial run claimed CPU only; external adoption remains unverified. +P5/P6 core execution and installation requirements are verified. P7 evaluates +engineering value against preregistered thresholds. + +**P6.1 — normal_fisher package.** Implement independent objective/schedule from +A/C, with finite-difference gradients, analytic Fisher references, and two-round +updates. Use public types/NumPy/CuPy; a registered builtin alias is not an external method. + +**P6.2 — bounded_leaves package.** Implement leaf rule B and prove clipping acts +during training on device, changes later raw/gradients, and survives storage. + +**P6.3 — Wheel conformance.** + +- Install OpenBoost and both independent wheels in fresh venvs; test outside the repo. +- Check actual module paths; extension imports are restricted to documented public modules. +- Run A+B+C together to verify coexistence, and separately for diagnosis. +- Remove extensions and load saved models for CPU raw inference; record build/lock/source hashes. +- README covers installation, execution, CPU oracle, Modal GPU checks, and actual + limits. Execute examples in tests. + +Acceptance: both packages work without private imports, forks, or manual core +changes; all three extension points change intended behavior. This establishes +technical conditions for adoption, not external adoption itself. + +## P7: Measure value, align documentation, and deliver + +**P7.1 — Matched-quality performance records.** + +- Compare P2 baseline and new defaults on the same Modal GPU, data/split/config. +- Separately record cold startup/compilation, warm median, end-to-end fit/predict, + peak device memory, transfers, and per-seed NLL/CRPS/coverage. +- Apply preregistered thresholds without changing seeds, removing datasets, or + relaxing limits after failure. +- Profile standard-path regression above 20%; after fixes rerun only affected + comparisons, without searching other GPUs for better numbers. +- External extensions compute different algorithms: report quality and cost + separately without requiring identical runtime to defaults. + +**P7.2 — Developer materials and documentation.** + +- Provide one page on implementing objectives/leaves/schedules, capability matrix, + actual failure examples, and reproduction commands. +- Prepare an external author task without its answer and a record sheet: time + to first correct output, help requests, private imports/core changes, GPU results, + and willingness to depend on OpenBoost in an independent package. +- Do not change the mission to predeclare platform success; explicitly leave G5 + incomplete without external attempts. + +**P7.3 — Final verification and conclusions.** + +- Run CPU regression, required CUDA correctness, changed-file lint, docs build, + and wheel-install conformance. +- Freeze the relationship between raw artifacts and final source SHA. Later + changes to relevant code cannot inherit earlier results automatically. +- Update learnings with passed gates, failures, unsupported parameters, and next + external verification action. +- Leave an explained workspace state and identify implementation commits; do not + automatically push or merge main. + +## Evidence file convention + +Suggested frozen structure; add precise ignore exceptions for new directories: + +```text +benchmarks/results/foundation// + manifest.json # source/wheel/data/env/argv/device provenance + results.json # per-test or per-seed raw results, includes failures + junit.xml # correctness jobs + transfer_summary.json # only when actually measured; declares coverage + README.md # reproduction, source commit, interpretation/limits +``` + +Keep necessary failure context without sensitive information; never commit full +credentials/environment dumps. Separate collection time from measured time. +Self-reported GPU labels do not establish device execution. Commit implementation +first, run from a clean SHA, then commit artifacts separately to avoid circular +source-hash references. + +## Startup instructions for the medium model + +> Continue `codex/gpu-python-foundation-design`. Read AGENTS.md, +> `planning/gpu-python-foundation-design.md`, and +> `planning/gpu-python-foundation-execution.md`. Execute from P0, reusing the remote +> unified trainer and preserving local correctness fixes. For each small task, +> write an independently justified failing test, implement, verify, update learning, +> inspect the diff, and commit. Modal is authorized for planned single-GPU checks: +> traceable smoke, then correctness, then benchmarks. Do not expand to multi-GPU +> or general training graphs, skip persistence, or count CPU fallback as GPU success. +> Do not push or merge main. Record falsifying evidence and revise the minimal +> design; distinguish technical acceptance from external adoption. + +## P7 engineering value review (2026-09-05) + +The [fixed matrix](../benchmarks/results/foundation/20260905T183820Z-3c245f2d/README.md) +preserves 12 cells, 48 timed fits, all seeds, and failure criteria. New default +GPU warm fit: 2.078694 s versus legacy GPU .149556 s, or 13.899x. Quality passed +for every fit/seed. Independent A+B+C proper scores were worse; coverage closer +to 90% alone cannot establish superiority. Original profiling ran concurrently +with memory sampling and contaminated host attribution. Collect isolated +diagnostics without replacing the original timing matrix. + +Conclusion boundary: retain the experimental extension API without replacing +the old GPU path or claiming speed/cost advantages. Further optimization must +preserve borrowed inputs, cached-prediction validation, and final quality. +Identify synchronization/validation costs before optimizing. Current evidence +does not support expanding multi-GPU, train-many, or a “general GPU Python +foundation” positioning. The historical product direction remains +calibration-first distributional risk modeling. + +The [author task and record sheet](../examples/extensions/AUTHOR_TASK.md) are ready, +but no external attempts or independent dependency willingness are evidenced. +G5 explicitly has not passed. The next adoption check is an actual trial, not +more self-authored examples. No outreach, push, publication, or external leaderboard update occurred. + +The [follow-up without concurrent sampling](../benchmarks/results/foundation/20260905T184856Z-dcd49569/README.md) +had 3 passed / 0 skipped and unchanged quality. Of the default path's diagnostic +2.260 s fit, tree/session boundaries used 2.123 s (builder internal 1.588 s), +and objective boundaries .0885 s. Investigate tree construction, repeated +validation, and synchronization before changing objective mathematics as the +main performance remedy. Nested synchronized measurements add overhead and +cannot replace uninstrumented results or predict an optimization's benefit. + +Final verification reuses P5's 904 CPU passes, P6 independent installation, and +real-CUDA conformance for the same unchanged core/plugin wheels. The added +harness passed 27 focused tests, production/changed-file lint, docs build, and +offline checks of both new artifacts. G4 performance budget failed. Exact peak +memory and full transfer traces remain gaps, not passes. + +### Fixed-slot optimization (2026-09-05) + +`81acf0b` preserves all checks while replacing dynamic boolean compression in +split application with fixed-shape selection and parent mappings for the frontier. +[Measurements](../benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md): +warm fit 2.079 -> 1.868 s; paired legacy-normalized ratio 13.899x -> 12.888x. +This is a partial improvement, not the 1.2 budget. Quality, real-CUDA conformance, +and independent-wheel loading passed. Retain the change; next investigate +tree/session validation and synchronization costs more precisely. + +### Bigger-goal review: immediate queue changed + +An earlier [impact/adoption/value review](impact-adoption-value-next.md) moved +the queue to ScoringBench. The user's subsequent clarification supersedes that +ordering: the foundation is the product, use cases drive its abstractions, and +backward compatibility is not required. Continue with the +[new F0–F5 plan](agent-boosting-foundation-plan.md); retain the old measurements. diff --git a/planning/impact-adoption-value-next.md b/planning/impact-adoption-value-next.md new file mode 100644 index 0000000..ca1dcb1 --- /dev/null +++ b/planning/impact-adoption-value-next.md @@ -0,0 +1,78 @@ +# Impact, adoption and value: next investment gate + +> Superseded by the user's foundation-first, no-backward-compatibility direction +> and [the active F0–F5 plan](agent-boosting-foundation-plan.md) on 2026-09-05. +> The text below records the earlier strategic interpretation, not the current +> work queue or mission. P7 falsifies its scoped performance target; it does not +> establish that the foundation product hypothesis has failed. The completed +> ScoringBench configuration fix remains useful evidence infrastructure. + +Review date: 2026-09-05. Replaces continued GPU micro-optimization as the immediate +work queue; the experimental API and its negative results remain available. + +## What the evidence changes + +The original goal is useful distributional risk modeling and independently usable +research tools. A high Python implementation ratio, more extension points and +passing our own tests are means, not measures of impact or adoption. + +- [P7 follow-up](../benchmarks/results/foundation/20260905T193308Z-5ebd75ab/README.md) + preserves quality and records a modest speed improvement, but the experimental + path still costs 12.888x the paired legacy CUDA fit. The general GPU foundation + investment gate failed; repeated small optimizations do not validate demand. +- CPU/CUDA agreement proves execution correctness. Housing's nominal 90% + intervals covering about 96% does not prove useful calibration. +- Independent package installation works, but repository-authored examples are + not external authors or evidence that anyone will depend on the package. + +## Priority queue + +| Objective | Next evidence | Decision enabled | +|---|---|---| +| Value | ScoringBench proper scores/calibration on a frozen external suite, with failures and reproducible configuration | Whether NaturalBoost offers useful predictive distributions beyond one Housing example | +| Adoption | Two outside developers attempt the prepared task; at least one completes their method without core edits | Whether the abstraction reduces their work and merits independent-package support | +| Impact | A reproducible exposure-aware risk case study after the benchmark integration is trustworthy | Whether the API addresses a real domain problem and can support independent research | + +Continue the verified legacy NaturalBoost CPU/CUDA paths for these experiments. +Keep the strict extension API experimental. Resume GPU foundation optimization +when a real method/workload needs it, with an explicit quality/cost target; do +not expand multi-GPU, train-many or API surface to compensate for missing users. + +## Immediate work: trustworthy third-party comparison + +1. **Constructor configuration (this slice):** forward CLI seed to OpenBoost and + XGBLSS, and depth/seed to a fresh clone of NGBoost's installed default learner. + Record constructed wrapper/base-learner parameters, not merely CLI intent. + Local tests must exercise seeded OpenBoost sampling, not only compare kwargs. +2. **Completion/provenance gate (next):** inspect upstream result-cache reuse, + reject incompatible prior configurations, and account for every requested + dataset/model/fold failure. Verify effective dataset row caps, hashes, split + identities and the pinned upstream commit. An empty or partial result is not + an official-quality completion. Keep upstream code unmodified. +3. **Bounded Linux integration run:** isolated installed wheel plus a pinned + ScoringBench environment; OpenBoost CPU and NGBoost, two-fold diabetes smoke. + Verify distribution/metric and result-schema behavior. This is not a ranking. +4. **Real quality shard, then full suite:** preregister parameters, seeds, full + expected matrix and resource budget; run one five-fold real-data shard, then + the remaining fixed suite after the pipeline passes. No test-fold tuning or + hiding failed datasets. Keep scale extensions separate from the official + protocol, and local results separate from upstream acceptance. + +The local ScoringBench checkout inspected for this review is clean at +`a938a667b7839b41e9272929010573410301c0b4`; that is the inspected revision, not +an assertion about the current upstream HEAD. The existing launcher is not yet +claimed ready for publication-quality full-suite evidence. Intel macOS must not +run its complete PyTorch/NumPy stack; use Linux for the full launcher. + +## Adoption boundary + +[The author task and record](../examples/extensions/AUTHOR_TASK.md) are ready. +Measure active/setup/blocked time, assistance, private imports/core edits, +mathematical correctness, GPU results if available and willingness to depend on +OpenBoost. Failed attempts remain evidence. No outside author has attempted the +trial in this work, and no invitation or message has been sent. Contacting authors, +publishing results and submitting a leaderboard entry require user authorization. + +Do not conflate the three gates: full internal correctness does not pass adoption; +a benchmark win does not establish user demand; a pleasant API does not establish +calibration or risk-model value. The mission in AGENTS.md is unchanged. diff --git a/planning/openboost-v1-evaluation.md b/planning/openboost-v1-evaluation.md new file mode 100644 index 0000000..4816a0b --- /dev/null +++ b/planning/openboost-v1-evaluation.md @@ -0,0 +1,209 @@ +# OpenBoost v1: Acceptance and evaluation protocol + +Version: v1-plan-r2 / 2026-09-05. Status: preregistered design thresholds; new v1 evaluation not run. +Scope revision: the user requires every application; individual A1–A13 replaces eight representative +tasks. Use with the [main plan](agent-boosting-foundation-plan.md). Numbers are proposed acceptance +criteria, not results. F0 freezes concrete data, implementations and resources before execution. +Postmeasurement threshold changes require a new protocol version; retain old failures. + +## 1. Result states and deliverables + +Every case records not_run/pass/fail/unsupported/error/timeout. Any non-pass required cell prevents +its gate passing. Optional unsupported records support boundary statements only. No hardware means +not_run, not GPU pass. A missing expected cell fails. + +F0 implements benchmarks/v1 manifests, runner/judge. Sprint 011 provides +[artifact integrity](../benchmarks/v1/README.md) for declared matrices/cache identities/files only. +Real freezes, runner and independent quality/E-gate judging remain incomplete; integrity_pass is not a gate pass. +Each immutable run directory includes at least: + +- manifest.json: protocol hash, code SHA/dirty, data version/hash/row/split IDs, target/preprocessing, + versions/wheel/build hashes, OS/CPU/RAM/threads, GPU/driver/CUDA, budgets, exact CLI, seeds, + A-ID→recipe/test/artifact mapping and expected task×model×fold×device matrix. +- cases.jsonl: cell status, effective parameters, backend/fallback, timing scope, prediction/model hashes, + metrics/failure reasons, not averages alone. Agent cases also record prompt/tool/model/budget hashes. +- report.json/README.md: gates, comparisons, worst cases, confidence intervals and unverified scope. +- Independent evaluators, complete stdout/stderr/JUnit or equivalent; verify parent-run hashes when referenced. + +First test judges with missing folds, changed-config stale caches, NaN, wrong backend, timeouts, skipped +GPUs, nonzero workers, duplicate cases and missing raw predictions. Training code cannot grade itself. +Cache keys include code, data splits, preprocessing, all configuration and protocol. + +## 2. E0: Coverage and interface semantics + +Required scope: R1–R9/C1–C7/A1–A13. Every item has interface, CPU/CUDA status, failure semantics, test +entry and artifact links: 100% recorded and 100% of required cells passing. Every A-ID appears in +manifest/report with a real task, implementation and independent check. Avoid meaningless Cartesian +products, but never substitute a few applications. Required cells cannot be deleted after results. +Capability support and task quality are separate and cannot offset each other. + +## 3. E1: Mathematical, state and persistence correctness + +| Category | Independent reference/counterexample | Default acceptance | +|---|---|---| +| Objective/geometry | Float64 formulas, finite differences, small solves; separate loss/gradient/effective curvature | Smooth well-conditioned fixtures rtol=1e-6, atol=1e-8; nonsmooth declared subgradient/leaf optimality, not inappropriate finite differences | +| Histogram/route/split | Original-row reductions, exhaustive candidates, missing/categories/empty/zero-weight/extra stats/ties | Integer counts/routing exact; CPU rtol=1e-7, atol=1e-9; declared tie policy | +| Leaves/topology | Weighted Newton/quantile, scalar/vector, three policies | Oracle match, at least two rounds and propagated state changes, not shapes alone | +| Run/update | Joint/ordered, backtracking/rejection, retry, early stop, RNG, M=1/8/32 permutations | Compare stable IDs; no rejection residue; reordering/grouping preserves seed semantics; retain failures | +| Persistence | Numeric/missing/categories/vectors/coefficients, output schema, offset/link, formula dependencies | New-process prediction roundtrip; corrupt shapes/versions/indices fail; old formats may be rejected | +| Data semantics | Query/entity splits, mappings, unseen, bounds, offset/weights | No cross-split learning, misalignment, double weights or silent fields; valid censoring infinity distinct from NaN | + +Deterministic CPU references rerun exactly under the same platform, threads, versions and seed. +GPU reductions need not be bitwise equal: float32 intermediates/default final raw use rtol=1e-4, +atol=1e-5; task metric difference<=1e-3*max(1, abs(CPU metric)). Extreme fixtures preregister separate +stable-reference tolerances; do not globally loosen them after failures. GPU topology may differ +within a predefined tie band if both candidates are within optimality tolerance and final quality +passes. Similar final scores cannot excuse wrong non-tied splits. + +## 4. E2: Algorithm expressiveness + +Runnable R1–R9 reference recipes use installed public v1 interfaces, no private imports. Verify: + +1. Replacement loss/parameter geometry, including an incumbent-friendly control. +2. Custom candidate feasibility/scoring/growth while reusing histogram/routing. +3. Leaf replacement, e.g. weighted residual quantiles, with routed rows/statistics available. +4. Multiparameter adaptive acceptance/rejection and changed parameter order. +5. Shared prepared data across runs with independent RNG, early stopping, errors/results. + +All five changes need mathematical/state checks, no core edits/private imports/task-name runner +branches. Independent package installation passes and replacements actually execute. This proves +expression, not new-algorithm quality or easier use than incumbents. + +## 5. E3: Real task quality and scope + +### Data coverage and splits + +F0 fixes a real task/acceptance entry for **each A1–A13**: regression, binary, multiclass, ranking, +quantiles, multioutput, Poisson, Gamma, Tweedie/composed total loss, survival/AFT, NaturalBoost, Formula +and train-many selection. A1–A12 each have predictive quality; A13 has real selection and full-set E4 cost. +Poisson cannot substitute for Gamma/Tweedie; ordinary regression cannot substitute for distributions/Formula. + +At least 6 independent sources total. Different targets on one dataset still count as one source; +relabeling/recounting predictions is not another use case. A13 reuse adds no source. Formula requires +synthetic identification/misspecification plus real quality. Before selecting real data, it remains +incomplete, not deferred. Mathematical simulation is E1, never real-task evidence. + +Sources come from [application contracts](foundation-application-contracts.md). F0 verifies downloads, +licenses/versions and pins IDs/raw hashes. Unavailable data stays unresolved; replication cannot invent +scale. Sources may change before evaluation, but all applications remain required; unsupported/not_run +cannot pass the application or E3. + +General splits: seeds 0–4,60/20/20 train/validation/test, using stratified/group/time logic as appropriate. +Official fixed splits take precedence. Ranking never crosses queries; duplicate entities never cross +splits; temporal tasks use declared rolling origins. Five runs need not be IID. Final test cannot +select parameters, budgets, thresholds or case exclusions. + +### Fair baselines + +Candidate versions: XGBoost 3.4.1, CatBoost 1.2.10, LightGBM 4.7.0; see [release review](boosting-release-review-2026-09-05.md). +Choose actual supported objectives per task, plus suitable GLM/AFT, NGBoost, Py-Boost or simple structural +baselines. Unsupported is not a worse score; do not require unsupported CatBoost SurvivalAft GPU. + +Retain explicit effective defaults. Main comparison uses **16 preregistered configurations per method**; +select configuration and baseline method on validation, then unlock test. Define reasonable per-method +spaces, not equal depth for symmetric/leaf-wise trees. Compare leaf budget/quality when matching size. +Report total model-selection cost. Fixed-trial and fixed-time conclusions are different experiments. +F0 freezes CPU/GPU/time/memory caps per trial; timeout is failure, not free extra retries. + +### Standard-recipe quality floor + +Compare against the validation-selected opponent, not posthoc best test scores. + +| Primary metric | Across-split threshold per task | +|---|---| +| Nonnegative loss: RMSE/log-loss/pinball/CRPS/deviance/Brier | Candidate/baseline median ratio<=1.05, worst<=1.15 | +| Potentially negative NLL/censored NLL | Align parameterization, units, normalization, constants; median per-row difference<=0.02 nats, worst<=0.10 | +| NDCG@10 | Baseline-minus-candidate median<=0.01, worst<=0.03 | + +Ratios only when baseline loss>1e-8; otherwise absolute difference<=1e-8 and mark near-perfect, +excluding from ratio summaries. Never take invalid geometric means of zero/negative values. +These are engineering gates, not universal noninferiority to three libraries. Every A1–A12 passes +its own gate; easy tasks cannot average away failures. A13 selected models pass the original task's +quality gate and selection-leakage checks. + +Distribution/survival tasks add proper scores/calibration. Report coverage with width; survival +respects censoring/support, not C-index alone. Metrics do not replace target semantics. Report every +split, paired differences and task-level intervals; small task sets do not establish market-wide superiority. + +Custom algorithms are reported separately. Mathematically correct but worse quality is a valid +research result, not a quality win. Foundation research value needs at least one E5 modification-cost +benefit. Algorithm superiority needs a matching real task, frozen protocol and reproducible quality gain. + +## 6. E4: GPU, train-many and inference cost + +Measure semantic reference, same-algorithm optimized implementation and quality-matched external +baselines. Same machine, threads, data/cache policy: one first-fit plus three warm fits, at least 3 seeds. +No profiler/GPU-memory sampling thread during official timing. Report binning, objectives, transfers, +JIT, training, validation, export/prediction; stage times supplement, not replace end-to-end. + +Required workloads: small startup/single-row prediction; medium/large real tasks from two sources, +one>=100k rows; K>1 outputs; M=1/8/32 ensembles. F0 fixes shapes/resources before results. Historical +T4 P7 stays separate with original 1.2 threshold, never rewritten. + +Cost gates: + +- Required device recipes have no silent fallback and pass all E1 CPU/CUDA checks. +- Two medium/large standard recipes: at matched quality, warm-fit median<=2× fastest qualifying + external GPU baseline. Either exceeding fails the GPU performance gate. +- Public composition/same-semantics optimized warm-fit ratio<=1.25, without ignoring plugins. +- Train-many: M=1 overhead<=10% versus independent path. Freeze at least one real ensemble and + measure M=8/32 against sequential shared-preprocessing reference. At least one reduces full-set + time>=20%; the other is no slower than 10%. Other required ensembles also no slower than 10%. + Per-run quality/seed/stop agree; memory meets fixed cap. Report binning-reuse-only benefits separately. +- Measure new-process import→load→predict and warm batch/single-row inference. Standard CPU median + ratio<=2 versus fastest qualifying CPU baseline. Declare custom dependencies. Manifest records + repeats, batch sizes and timer-overhead correction. + +Failure may still yield CPU/experimental work, but not v1 GPU acceptance. Not every research recipe +must share one speed target, yet complete costs are recorded. Exact GPU peak, device-wide sampled +lower bounds and process RSS differ. Missing values stay null with explanations, never zero-as-no-memory. + +## 7. E5: Agent evaluation + +Choose one development task from each E2 modification type and two tasks excluded from API design. +Each has a card, math/state verifier and resource limit; candidates cannot modify judges. Tasks used +for interface changes become development, not unseen validation. + +Compare OpenBoost against **F0's most appropriate existing implementation path**, allowing public +hooks, outer loops or source edits with normal docs/recipes/tools. Include Py-Boost/GPU custom-objective +capabilities in selection rather than weak defaults. NumPy can be a separately reported diagnostic control. + +Each task/arm has **3 independent attempts**: 15 development and 6 held-out. Freeze agent model/reasoning, +tools, initial docs, cache/prompts. Each attempt caps at **30 minutes or 20k generated tokens**, whichever +comes first; manifest states token accounting/service measurability. Rotate arm order, no patch leakage. +Before large execution, run one smoke task to verify accounting/cost. + +Record time to first correct completion, install/active/blocked time, tokens/tool/compute cost, human +hints, core edits/private imports, failure reasons and independent correctness. Failed attempts count +full budget in time aggregates; also report successful-only time, not success-only selection. Changed +versions/settings require a separate cohort. + +Gates: OpenBoost development>=12/15 correct and>=2/3 per task; held-outs>=2/3 each, total>=4/6. +No core edits/private dependencies. At least two deep-change types reduce capped median time>=30% +versus the opponent, with no fewer total correct completions. Equivalent controls need not be wins. +These are small-sample engineering decisions, not significance claims. On failure inspect task/docs/API +boundaries, not merely add hints or restrict opponents. + +## 8. E6: Installation, delivery and reproducibility + +- Clean CPU and one CUDA environment install wheels without source/private-import dependencies. + CPU install does not require CUDA; declared Python/OS matrix passes individually. +- Two independent extension packages plus all standard recipes run. Core raw/tree inference works + after training plugins are removed. Custom formulas/links require declared installed dependencies, + not automatic arbitrary-closure serialization. +- Docs, signatures, capability tables/tests agree. Major errors identify component/field/device/shape/reason. +- Judges reconstruct gates from committed raw artifacts; runtime failures produce nonzero exit. +- Release content has versions/licenses and no private data. Old-format rejection is allowed; + new round trips must work. Publishing, push and leaderboards still require user-requested external action. + +**Engineering v1 complete: individual A1–A13 evidence and every required E0–E6 gate passing.** +Partial work is a milestone/candidate, not full v1. Reports list gate evidence, not total tests as completion. + +## 9. E7: Adoption/impact independent of engineering completion + +After authorized contact, at least two independent authors try the package; one implements their +own method and reuses it on another task. Record installation, help, failures, reasons and actual +dependencies. Internal agents/repository authors are not external users. Impact means independent +research reuse, downstream method packages or real decision benefits, not stars/downloads alone. +CPU trials can begin before all GPU gates. Until E7 passes, adoption remains an unverified hypothesis, +even if E0–E6 pass; do not claim an ecosystem or broad product value. diff --git a/planning/unified-engine-design.md b/planning/unified-engine-design.md index b2d9782..a226afa 100644 --- a/planning/unified-engine-design.md +++ b/planning/unified-engine-design.md @@ -1,6 +1,8 @@ # Unified GPU Boosting Engine — Design -Status: Phase A+B implemented; A100 speed gate passed (1229x vs NGBoost at 90K) · 2026-08-17 +Status: Historical design from 2026-08-17. Implementation integrated in P0. +Historical performance statements below are unverified notes, not current +passed gates; see `docs/benchmarks.md` and the foundation execution plan. Evidence: `development/paramboost/spike_ggn.py` (GGN spike), CustomDistribution/loop survey, NaturalBoost GPU-boundary survey. diff --git a/pyproject.toml b/pyproject.toml index 4666fac..8157427 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ build-backend = "hatchling.build" [project] name = "openboost" -version = "1.0.0rc1" -description = "GPU gradient boosting for distributional regression and varying-coefficient models" +version = "1.0.0.dev0" +description = "Programmable boosting foundation for researchers and AI agents (under construction)" readme = "README.md" license = "Apache-2.0" requires-python = ">=3.10" @@ -25,7 +25,7 @@ keywords = [ "machine-learning", ] classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: Apache Software License", @@ -37,38 +37,19 @@ classifiers = [ ] dependencies = [ "numpy>=1.24", - "numba>=0.60", - "joblib>=1.3", - "scipy>=1.10", ] [project.optional-dependencies] -# GPU backends +# Planned CUDA development dependencies; the reset namespace has no GPU API yet. cuda = [ "numba-cuda>=0.23", "cupy-cuda12x>=13.0", ] -torch = [ - "torch>=2.0", -] -jax = [ - "jax[cuda12]>=0.4", -] -# Distributed/Multi-GPU training (Phase 12, 18) -distributed = [ - "ray[default]>=2.0", -] -# sklearn-compatible wrappers (Phase 13) -sklearn = [ - "scikit-learn>=1.0", -] -# Testing (plain jax so the CustomDistribution autodiff path runs in CPU CI; -# linux-only marker because recent jaxlib ships no macOS-Intel wheels) +# Independent reference tests, without retired production/autodiff dependencies. test = [ "pytest>=7.0", "pytest-cov>=4.0", "pytest-xdist>=3.0", - "jax>=0.4; sys_platform == 'linux'", ] # Benchmarking bench = [ @@ -78,13 +59,9 @@ bench = [ ] # Development dev = [ - "openboost[test,bench,sklearn]", + "openboost[test,bench]", "ruff>=0.4", ] -# Full installation (all optional features) -all = [ - "openboost[cuda,torch,distributed,sklearn,dev]", -] [project.urls] Homepage = "https://github.com/jxucoder/openboost" @@ -110,7 +87,7 @@ ignore = [ ] [tool.pytest.ini_options] -testpaths = ["tests"] +testpaths = ["tests/v1"] addopts = "-v --tb=short -n auto --dist loadfile" markers = [ "slow: marks tests that take >10s (deselect with '-m \"not slow\"')", diff --git a/src/openboost/__init__.py b/src/openboost/__init__.py index 8bfd74b..a201ef0 100644 --- a/src/openboost/__init__.py +++ b/src/openboost/__init__.py @@ -1,508 +1,23 @@ -"""OpenBoost: GPU gradient boosting for distributional regression. - -Every parameter of F(y|x) gets its own ensemble of histogram trees, updated -with the full K x K natural gradient: GAMLSS-style distributional regression -(NaturalBoost, plus WeibullAFT for right-censored data) and varying-coefficient -models (FormulaBoost). - - >>> import openboost as ob - >>> model = ob.NaturalBoostNormal(n_trees=100) - >>> model.fit(X_train, y_train) - >>> mean = model.predict(X_test) - >>> lo, hi = model.predict_interval(X_test, alpha=0.1) - - >>> model = ob.FormulaBoost(formula=f, n_params=2, links=("log", "identity")) - >>> model.fit(Z, y, model_input=x) - - >>> model = ob.WeibullAFT() - >>> model.fit(Z, time, event=observed) +"""Programmable boosting foundation: initial CPU records and state transactions. + +Numeric inputs, mapped ensemble artifacts and explicit run state are public. +Numeric/categorical operations support depthwise, best-first and symmetric growth. +Squared, Normal, Formula, binary and multiclass CPU recipes are public. +Vector leaves, separate split/leaf statistics and sequential runs are available. +Query-local pairwise/lambda ranking composes the same scalar tree operations. +Routed residual views support quantile and anchored penalized leaves. +Poisson counts use explicit exposure and rate/count transforms. +Gamma positive-target mean regression shares scalar Newton operations. +Fixed-power Tweedie supports nonnegative mean regression. +Frequency/severity composition preserves two-model output roles and units. +Fixed-scale AFT supports event/right censoring and persisted survival outputs. +Multi-output squared recipes support independent/shared topology and target scaling. +Explicit prepared training data can be shared across independent CPU runs. +Public StopState separates validation patience from model acceptance in all recipes. +CUDA execution is not implemented yet. """ -import warnings as _warnings - -__version__ = "1.0.0rc1" - -# ============================================================================= -# Data Layer -# ============================================================================= -from ._array import MISSING_BIN, BinnedArray, array, as_numba_array -from ._batch import BatchTrainingState, ConfigBatch - -# ============================================================================= -# Core (Foundation) -# ============================================================================= -from ._core import ( - # Growth strategies (Phase 8.2) - GrowthConfig, - GrowthStrategy, - # Leaf value abstractions (Phase 9.0) - LeafValues, - LeafWiseGrowth, - LevelWiseGrowth, - # Primitives (Phase 8.1) - NodeHistogram, - NodeSplit, - ScalarLeaves, - SymmetricGrowth, - # Symmetric trees - SymmetricTree, - TreeNode, - TreeStructure, - VectorLeaves, - build_node_histograms, - compute_leaf_values, - find_node_splits, - # Tree building - fit_tree, - fit_tree_gpu_native, - fit_tree_symmetric, - fit_tree_symmetric_gpu_native, - fit_trees_batch, - get_children, - get_growth_strategy, - get_nodes_at_depth, - get_parent, - init_sample_node_ids, - partition_samples, - # Prediction - predict_ensemble, - predict_symmetric_tree, - predict_tree, - subtract_histogram, -) -from ._core import ( - Tree as LegacyTree, -) -from ._core._growth import register_growth_strategy - -# Phase 8: TreeStructure is the new Tree -Tree = TreeStructure # Alias for backward compatibility - -# ============================================================================= -# Models (High-Level) -# ============================================================================= -# ============================================================================= -# Backend Control -# ============================================================================= -from ._backends import ( - backend_context, - clear_tree_workspace_cache, - get_backend, - is_cpu, - is_cuda, - set_backend, -) - -# ============================================================================= -# Callbacks (Phase 13) -# ============================================================================= -from ._callbacks import ( - Callback, - CallbackManager, - EarlyStopping, - HistoryCallback, - LearningRateScheduler, - Logger, - ModelCheckpoint, - TrainingState, -) - -# ============================================================================= -# Multi-GPU Training (Phase 18) -# ============================================================================= -from ._distributed import ( - GPUWorker, - GPUWorkerBase, - MultiGPUContext, - fit_tree_multigpu, -) - -# ============================================================================= -# Distributions (Phase 15) -# ============================================================================= -from ._distributions import ( - # Custom distributions with autodiff - CustomDistribution, - Distribution, - DistributionOutput, - Gamma, - LogNormal, - NegativeBinomial, - Normal, - Poisson, - StudentT, - # Kaggle competition favorites - Tweedie, - create_custom_distribution, - get_distribution, - list_distributions, - register_distribution, -) - -# ============================================================================= -# Feature Importance (Phase 13) -# ============================================================================= -from ._importance import ( - compute_feature_importances, - get_feature_importance_dict, - plot_feature_importances, -) - -# ============================================================================= -# Loss Functions -# ============================================================================= -from ._loss import ( - device_loss, - gamma_gradient, # Phase 9.3 - get_loss_function, - huber_gradient, - logloss_gradient, - mae_gradient, # Phase 9.1 - mse_gradient, - poisson_gradient, # Phase 9.3 - quantile_gradient, # Phase 9.1 - register_loss, - softmax_gradient, # Phase 9.2 - tweedie_gradient, # Phase 9.3 -) -from ._models import ( - DART, - # Phase 15/16: Distributional GBDT (NaturalBoost) - DistributionalGBDT, - FormulaBoost, - GradientBoosting, - # Phase 15: Linear Leaf GBDT - LinearLeafGBDT, - MultiClassGradientBoosting, - NaturalBoost, - NaturalBoostGamma, - NaturalBoostLogNormal, - NaturalBoostNegBin, - NaturalBoostNormal, - NaturalBoostPoisson, - NaturalBoostStudentT, - NaturalBoostTweedie, - OpenBoostClassifier, - # Phase 15+: sklearn wrappers for new models - OpenBoostDARTRegressor, - OpenBoostDistributionalRegressor, - OpenBoostGAM, - OpenBoostGAMRegressor, - OpenBoostLinearLeafRegressor, - # Phase 13: sklearn-compatible wrappers - OpenBoostRegressor, - WeibullAFT, -) -from ._models import ( - # Backward compatibility aliases (deprecated, accessed via __getattr__) - NGBoost as _NGBoost, -) -from ._models import ( - NGBoostGamma as _NGBoostGamma, -) -from ._models import ( - NGBoostLogNormal as _NGBoostLogNormal, -) -from ._models import ( - NGBoostNegBin as _NGBoostNegBin, -) -from ._models import ( - NGBoostNormal as _NGBoostNormal, -) -from ._models import ( - NGBoostPoisson as _NGBoostPoisson, -) -from ._models import ( - NGBoostStudentT as _NGBoostStudentT, -) -from ._models import ( - NGBoostTweedie as _NGBoostTweedie, -) -from ._models._sklearn import OpenBoostGAMClassifier - -# ============================================================================= -# Persistence -# ============================================================================= -from ._persistence import load -from ._profiler import ProfilingCallback - -# ============================================================================= -# Sampling Strategies (Phase 17) -# ============================================================================= -from ._sampling import ( - GOSSConfig, - MiniBatchConfig, - MiniBatchIterator, - SamplingResult, - SamplingStrategy, - accumulate_histograms_minibatch, - apply_sampling, - create_memmap_binned, - goss_sample, - load_memmap_binned, - random_sample, -) - -# ============================================================================= -# Utilities (Phase 20.6) -# ============================================================================= -# ============================================================================= -# Evaluation Metrics (Phase 22) -# ============================================================================= -# ============================================================================= -# Probabilistic/Distributional Metrics (Phase 22 Sprint 2) -# ============================================================================= -from ._utils import ( - PARAM_GRID_CLASSIFICATION, - PARAM_GRID_DISTRIBUTIONAL, - PARAM_GRID_REGRESSION, - PITRecalibrator, - accuracy_score, - brier_score, - calibration_curve, - cross_val_predict, - cross_val_predict_interval, - cross_val_predict_proba, - crps_empirical, - crps_gaussian, - evaluate_coverage, - expected_calibration_error, - f1_score, - get_param_grid, - interval_score, - log_loss_score, - mae_score, - mse_score, - negative_log_likelihood, - pinball_loss, - pit_histogram, - pit_values, - precision_score, - r2_score, - recalibrate_pit, - recall_score, - reliability_diagram, - rmse_score, - roc_auc_score, - suggest_params, -) - -_DEPRECATED_ALIASES = { - "NGBoost": ("NaturalBoost", _NGBoost), - "NGBoostNormal": ("NaturalBoostNormal", _NGBoostNormal), - "NGBoostLogNormal": ("NaturalBoostLogNormal", _NGBoostLogNormal), - "NGBoostGamma": ("NaturalBoostGamma", _NGBoostGamma), - "NGBoostPoisson": ("NaturalBoostPoisson", _NGBoostPoisson), - "NGBoostStudentT": ("NaturalBoostStudentT", _NGBoostStudentT), - "NGBoostTweedie": ("NaturalBoostTweedie", _NGBoostTweedie), - "NGBoostNegBin": ("NaturalBoostNegBin", _NGBoostNegBin), -} - - -def __getattr__(name: str): - if name in _DEPRECATED_ALIASES: - new_name, obj = _DEPRECATED_ALIASES[name] - _warnings.warn( - f"{name} is deprecated, use {new_name} instead", - DeprecationWarning, - stacklevel=2, - ) - return obj - raise AttributeError(f"module 'openboost' has no attribute {name!r}") - +from .data import ClassSchema, MixedData, NumericData, Problem +from .runtime import RunContext -__all__ = [ - # Version - "__version__", - # Data - "array", - "BinnedArray", - "as_numba_array", - "MISSING_BIN", - # High-level API (recommended) - "GradientBoosting", - "MultiClassGradientBoosting", - "OpenBoostGAM", - "DART", - # Phase 15/16: Distributional GBDT (NaturalBoost) - "DistributionalGBDT", - "NaturalBoost", - "NaturalBoostNormal", - "NaturalBoostLogNormal", - "NaturalBoostGamma", - "NaturalBoostPoisson", - "NaturalBoostStudentT", - "NaturalBoostTweedie", - "NaturalBoostNegBin", - "FormulaBoost", - "WeibullAFT", - "LegacyTree", - # Backward compatibility (deprecated) - "NGBoost", - "NGBoostNormal", - "NGBoostLogNormal", - "NGBoostGamma", - "NGBoostPoisson", - "NGBoostStudentT", - "NGBoostTweedie", - "NGBoostNegBin", - # Phase 15: Linear Leaf GBDT - "LinearLeafGBDT", - # Phase 15: Distributions - "Distribution", - "DistributionOutput", - "Normal", - "LogNormal", - "Gamma", - "Poisson", - "StudentT", - # Kaggle competition favorites - "Tweedie", - "NegativeBinomial", - # Custom distributions - "CustomDistribution", - "create_custom_distribution", - "get_distribution", - "list_distributions", - # sklearn-compatible wrappers (Phase 13 + 15) - "OpenBoostRegressor", - "OpenBoostClassifier", - "OpenBoostDARTRegressor", - "OpenBoostGAMRegressor", - "OpenBoostGAMClassifier", - "OpenBoostDistributionalRegressor", - "OpenBoostLinearLeafRegressor", - # Callbacks (Phase 13) - "Callback", - "EarlyStopping", - "Logger", - "ModelCheckpoint", - "LearningRateScheduler", - "HistoryCallback", - "CallbackManager", - "TrainingState", - "ProfilingCallback", - # Feature importance (Phase 13) - "compute_feature_importances", - "get_feature_importance_dict", - "plot_feature_importances", - # Loss functions - "mse_gradient", - "logloss_gradient", - "huber_gradient", - "mae_gradient", - "quantile_gradient", - "poisson_gradient", - "gamma_gradient", - "tweedie_gradient", - "softmax_gradient", - "get_loss_function", - # Extensibility: registration APIs - "register_loss", - "register_distribution", - "register_growth_strategy", - "device_loss", - # Training (single tree, low-level) - "fit_tree", - "fit_tree_gpu_native", - "Tree", - # Training (symmetric/oblivious trees) - "fit_tree_symmetric", - "fit_tree_symmetric_gpu_native", - "SymmetricTree", - "TreeNode", - "predict_symmetric_tree", - # Training (batch, low-level) - "fit_trees_batch", - "ConfigBatch", - "BatchTrainingState", - # Tree building primitives (Phase 8.1) - "NodeHistogram", - "NodeSplit", - "build_node_histograms", - "subtract_histogram", - "find_node_splits", - "partition_samples", - "compute_leaf_values", - "init_sample_node_ids", - "get_nodes_at_depth", - "get_children", - "get_parent", - # Growth strategies (Phase 8.2) - "GrowthConfig", - "GrowthStrategy", - "TreeStructure", - "LevelWiseGrowth", - "LeafWiseGrowth", - "SymmetricGrowth", - "get_growth_strategy", - # Leaf value abstractions (Phase 9.0) - "LeafValues", - "ScalarLeaves", - "VectorLeaves", - # Prediction - "predict_tree", - "predict_ensemble", - # Backend - "backend_context", - "clear_tree_workspace_cache", - "get_backend", - "set_backend", - # Persistence - "load", - "is_cuda", - "is_cpu", - # Sampling (Phase 17) - "SamplingStrategy", - "GOSSConfig", - "MiniBatchConfig", - "SamplingResult", - "goss_sample", - "random_sample", - "apply_sampling", - "MiniBatchIterator", - "accumulate_histograms_minibatch", - "create_memmap_binned", - "load_memmap_binned", - # Multi-GPU (Phase 18) - "MultiGPUContext", - "GPUWorkerBase", - "GPUWorker", - "fit_tree_multigpu", - # Utilities (Phase 20.6) - "suggest_params", - "cross_val_predict", - "cross_val_predict_proba", - "cross_val_predict_interval", - "evaluate_coverage", - "get_param_grid", - "PARAM_GRID_REGRESSION", - "PARAM_GRID_CLASSIFICATION", - "PARAM_GRID_DISTRIBUTIONAL", - # Evaluation Metrics (Phase 22) - "roc_auc_score", - "accuracy_score", - "log_loss_score", - "mse_score", - "r2_score", - "mae_score", - "rmse_score", - "f1_score", - "precision_score", - "recall_score", - # Probabilistic/Distributional Metrics (Phase 22 Sprint 2) - "crps_gaussian", - "crps_empirical", - "brier_score", - "pinball_loss", - "interval_score", - "expected_calibration_error", - "calibration_curve", - "negative_log_likelihood", - # PIT calibration diagnostics - "pit_values", - "pit_histogram", - "reliability_diagram", - "recalibrate_pit", - "PITRecalibrator", -] +__all__ = ("ClassSchema", "MixedData", "NumericData", "Problem", "RunContext") diff --git a/src/openboost/_array.py b/src/openboost/_array.py deleted file mode 100644 index 24bea82..0000000 --- a/src/openboost/_array.py +++ /dev/null @@ -1,551 +0,0 @@ -"""Array handling and binning for OpenBoost. - -Provides `ob.array()` for converting data to the internal binned format. - -Phase 14: Added missing value handling (NaN -> bin 255). -Phase 14.3: Added native categorical feature support. -""" - -from __future__ import annotations - -import warnings -from collections.abc import Sequence -from dataclasses import dataclass, field -from typing import TYPE_CHECKING - -import numpy as np - -from ._backends import get_backend, is_cuda - -if TYPE_CHECKING: - from numpy.typing import ArrayLike, NDArray - - -# Reserved bin index for missing values (NaN) -MISSING_BIN: int = 255 - - -@dataclass -class BinnedArray: - """Binned feature matrix ready for tree building. - - Attributes: - data: Binned data, shape (n_features, n_samples), dtype uint8 - NaN values are encoded as bin 255 (MISSING_BIN) - bin_edges: List of bin edges per feature, for inverse transform - n_features: Number of features - n_samples: Number of samples - device: "cuda" or "cpu" - has_missing: Boolean array (n_features,) indicating which features have NaN - is_categorical: Boolean array (n_features,) indicating categorical features - category_maps: List of dicts mapping original values -> bin indices (None for numeric) - n_categories: Number of categories per feature (0 for numeric) - """ - data: NDArray[np.uint8] # Or DeviceNDArray for CUDA - bin_edges: list[NDArray[np.float64]] - n_features: int - n_samples: int - device: str - has_missing: NDArray[np.bool_] = field(default_factory=lambda: np.array([], dtype=np.bool_)) - # Phase 14.3: Categorical support - is_categorical: NDArray[np.bool_] = field(default_factory=lambda: np.array([], dtype=np.bool_)) - category_maps: list[dict | None] = field(default_factory=list) - n_categories: NDArray[np.int32] = field(default_factory=lambda: np.array([], dtype=np.int32)) - - def __repr__(self) -> str: - n_missing = int(np.sum(self.has_missing)) if len(self.has_missing) > 0 else 0 - n_cat = int(np.sum(self.is_categorical)) if len(self.is_categorical) > 0 else 0 - return ( - f"BinnedArray(n_features={self.n_features}, n_samples={self.n_samples}, " - f"device={self.device!r}, features_with_missing={n_missing}, " - f"categorical_features={n_cat})" - ) - - @property - def any_missing(self) -> bool: - """Check if any feature has missing values.""" - return len(self.has_missing) > 0 and np.any(self.has_missing) - - @property - def any_categorical(self) -> bool: - """Check if any feature is categorical.""" - return len(self.is_categorical) > 0 and np.any(self.is_categorical) - - def transform(self, X: ArrayLike) -> BinnedArray: - """Transform new data using the bin edges from this BinnedArray. - - Use this method to transform test/validation data using the same - binning learned from training data. This ensures tree splits work - correctly across train and test sets. - - Args: - X: New input features, shape (n_samples_new, n_features). - Must have the same number of features as the training data. - - Returns: - BinnedArray with new data binned using training bin edges. - - Example: - >>> X_train_binned = ob.array(X_train) - >>> model.fit(X_train_binned, y_train) - >>> X_test_binned = X_train_binned.transform(X_test) - >>> predictions = model.predict(X_test_binned) - """ - # Convert to numpy - X_np = _to_numpy(X) - if X_np.ndim == 1: - X_np = X_np.reshape(-1, 1) - - n_samples_new, n_features_new = X_np.shape - - if n_features_new != self.n_features: - raise ValueError( - f"X has {n_features_new} features, but BinnedArray was fitted with " - f"{self.n_features} features" - ) - - # Bin each feature using existing bin edges - binned = np.zeros((n_samples_new, self.n_features), dtype=np.uint8) - - for j in range(self.n_features): - col = X_np[:, j].astype(np.float64) - nan_mask = np.isnan(col) - - if self.is_categorical[j] if len(self.is_categorical) > j else False: - # Categorical feature: use category map - cat_map = self.category_maps[j] if j < len(self.category_maps) else None - if cat_map is not None: - for i, val in enumerate(col): - if np.isnan(val): - binned[i, j] = MISSING_BIN - else: - # Try the raw numeric value first, then int form - # to match however the key was stored at fit time - key = val.item() if hasattr(val, 'item') else val - if key not in cat_map: - key = int(val) if np.isfinite(val) else val - if key in cat_map: - binned[i, j] = cat_map[key] - else: - warnings.warn( - f"Unseen category {key!r} in feature {j}; mapping to MISSING_BIN", - stacklevel=2, - ) - binned[i, j] = MISSING_BIN - else: - binned[:, j] = 0 - else: - # Numeric feature: use bin edges with searchsorted - edges = self.bin_edges[j] - if len(edges) == 0: - # No bin edges (constant feature) - binned[:, j] = 0 - else: - # searchsorted finds the bin index - bin_idx = np.searchsorted(edges, col[~nan_mask], side='right') - # Clip to valid range (in case test values exceed training range) - bin_idx = np.clip(bin_idx, 0, len(edges) - 1) - binned[~nan_mask, j] = bin_idx.astype(np.uint8) - - # Handle missing values - if np.any(nan_mask): - binned[nan_mask, j] = MISSING_BIN - - # Transpose to feature-major layout - binned = np.ascontiguousarray(binned.T) - - # Move to device if needed - if self.device == "cuda": - from numba import cuda - binned = cuda.to_device(binned) - - return BinnedArray( - data=binned, - bin_edges=self.bin_edges, # Keep same bin edges - n_features=self.n_features, - n_samples=n_samples_new, - device=self.device, - has_missing=self.has_missing, - is_categorical=self.is_categorical, - category_maps=self.category_maps, - n_categories=self.n_categories, - ) - - -def array( - X: ArrayLike, - n_bins: int = 254, - *, - categorical_features: Sequence[int] | None = None, - device: str | None = None, -) -> BinnedArray: - """Convert input data to binned format for tree building. - - This is the primary entry point for data. Binning is done once, - then the binned data can be used for training many models. - - Missing values (NaN) are automatically detected and encoded as bin 255. - The model learns the optimal direction for missing values at each split. - - Categorical features use native category encoding instead of quantile binning, - enabling the model to learn optimal category groupings. - - Args: - X: Input features, shape (n_samples, n_features) - Accepts numpy arrays, PyTorch tensors, JAX arrays, CuPy arrays. - NaN values are handled automatically. - n_bins: Maximum number of bins for numeric features (max 255). - categorical_features: List of column indices that are categorical. - These use category encoding instead of quantile binning. - Max 254 unique categories per feature (255 reserved for NaN). - device: Target device ("cuda" or "cpu"). Auto-detected if None. - - Returns: - BinnedArray with binned data in feature-major layout (n_features, n_samples). - NaN values are encoded as MISSING_BIN (255). - - Example: - >>> import openboost as ob - >>> import numpy as np - >>> - >>> # Numeric features with missing values - >>> X = np.array([[1.0, np.nan], [2.0, 3.0], [np.nan, 4.0]]) - >>> X_binned = ob.array(X) - >>> print(X_binned.has_missing) # [True, True] - >>> - >>> # Mixed numeric and categorical - >>> X = np.array([[25, 0, 50000], [30, 1, 60000], [35, 2, 70000]]) - >>> X_binned = ob.array(X, categorical_features=[1]) # Feature 1 is categorical - >>> print(X_binned.is_categorical) # [False, True, False] - """ - # Reserve bin 255 for missing values — max usable bin is 254 (indices 0-254) - if n_bins > 254: - warnings.warn( - f"n_bins={n_bins} exceeds maximum of 254 (bin 255 is reserved for missing values). " - "Clamping to 254.", - stacklevel=2, - ) - n_bins = 254 - - # Convert to numpy for binning computation - X_np = _to_numpy(X) - - if X_np.ndim != 2: - raise ValueError(f"X must be 2D (n_samples, n_features), got shape {X_np.shape}") - - n_samples, n_features = X_np.shape - - # Handle categorical features - categorical_set = set(categorical_features) if categorical_features else set() - - # Validate categorical indices - for idx in categorical_set: - if idx < 0 or idx >= n_features: - raise ValueError(f"categorical_features index {idx} out of range [0, {n_features})") - - # Auto-detect string/object columns as categorical. - # NOTE: test via dtype.kind, not `is object` — a numpy/pandas dtype is a - # distinct object from the builtin `object` (np.dtype('O') is object -> False), - # so an identity check silently misses string/object columns. dtype.kind is - # 'O' for object, 'U'/'S' for fixed-width unicode/bytes strings. - if hasattr(X, 'dtypes'): - # DataFrame: check each column's dtype - for i, dtype in enumerate(X.dtypes): - if getattr(dtype, 'kind', '') in ('O', 'U', 'S') or hasattr(dtype, 'categories'): - categorical_set.add(i) - elif X_np.dtype.kind in ('O', 'U', 'S'): - # Object/string array: check which columns are non-numeric - for i in range(n_features): - col = X_np[:, i] - try: - col.astype(np.float64) - except (ValueError, TypeError): - categorical_set.add(i) - - # Process features (numeric vs categorical) - # Returns feature-major layout: (n_features, n_samples) — no transpose needed - binned, bin_edges, has_missing, is_categorical, category_maps, n_categories = _bin_features( - X_np, n_bins, categorical_set - ) - - # Determine device - if device is None: - device = get_backend() - - # Transfer to GPU if needed - if device == "cuda" and is_cuda(): - from ._backends._cuda import to_device - binned = to_device(binned) - - return BinnedArray( - data=binned, - bin_edges=bin_edges, - n_features=n_features, - n_samples=n_samples, - device=device, - has_missing=has_missing, - is_categorical=is_categorical, - category_maps=category_maps, - n_categories=n_categories, - ) - - -def _to_numpy(arr: ArrayLike) -> NDArray: - """Convert various array types to numpy. - - Handles: numpy, pandas DataFrame, PyTorch, JAX, CuPy - """ - # Already numpy - if isinstance(arr, np.ndarray): - return arr - - # Pandas DataFrame — use .to_numpy() to preserve dtypes - if hasattr(arr, 'to_numpy') and hasattr(arr, 'dtypes'): - return arr.to_numpy() - - # PyTorch - if hasattr(arr, 'cpu') and hasattr(arr, 'numpy'): - return arr.cpu().numpy() - - # JAX (has __array__ protocol) - if hasattr(arr, '__array__'): - return np.asarray(arr) - - # CuPy - if hasattr(arr, 'get'): - return arr.get() - - # Fallback - return np.asarray(arr) - - -def _bin_features( - X: NDArray, - n_bins: int, - categorical_set: set[int], -) -> tuple[NDArray[np.uint8], list[NDArray[np.float64]], NDArray[np.bool_], - NDArray[np.bool_], list[dict | None], NDArray[np.int32]]: - """Bin features (numeric and categorical) with missing value handling. - - Args: - X: Input data, shape (n_samples, n_features) - n_bins: Number of bins for numeric features - categorical_set: Set of indices for categorical features - - Returns: - binned: Binned data, shape (n_features, n_samples), uint8 (feature-major) - bin_edges: List of bin edges per feature (empty for categorical) - has_missing: Boolean array (n_features,) for missing values - is_categorical: Boolean array (n_features,) for categorical features - category_maps: List of dicts (None for numeric) - n_categories: Number of categories per feature (0 for numeric) - """ - from joblib import Parallel, delayed - - n_samples, n_features = X.shape - - # Pre-compute percentiles for numeric features - percentiles = np.linspace(0, 100, n_bins + 1)[1:-1] - - # Pre-allocate in feature-major layout to avoid column_stack + transpose - binned = np.empty((n_features, n_samples), dtype=np.uint8) - - def bin_single_feature(f: int): - """Bin a single feature column, writing directly to pre-allocated output.""" - col = X[:, f] - is_cat = f in categorical_set - - result = _bin_categorical_feature(col) if is_cat else _bin_numeric_feature(col, percentiles) - # Write binned values directly into feature-major row - binned[f, :] = result[0] - return result[1:] # edges, has_nan, is_cat, category_map, n_categories - - # Parallel processing across features - results = Parallel(n_jobs=-1, prefer="threads")( - delayed(bin_single_feature)(f) for f in range(n_features) - ) - - # Combine metadata (binned data already written in-place) - bin_edges = [r[0] for r in results] - has_missing = np.array([r[1] for r in results], dtype=np.bool_) - is_categorical = np.array([r[2] for r in results], dtype=np.bool_) - category_maps = [r[3] for r in results] - n_categories = np.array([r[4] for r in results], dtype=np.int32) - - return binned, bin_edges, has_missing, is_categorical, category_maps, n_categories - - -def _bin_numeric_feature( - col: NDArray, - percentiles: NDArray, -) -> tuple[NDArray[np.uint8], NDArray[np.float64], bool, bool, dict | None, int]: - """Bin a numeric feature using quantiles. - - Returns: - binned_col, bin_edges, has_nan, is_categorical, category_map, n_categories - """ - # Work in the input dtype (typically float32) — avoids float64 copy - if not np.issubdtype(col.dtype, np.floating): - col = col.astype(np.float32) - - # Detect missing values - nan_mask = np.isnan(col) - has_nan = bool(np.any(nan_mask)) - - # Initialize output - binned_col = np.zeros(len(col), dtype=np.uint8) - - if has_nan: - valid_col = col[~nan_mask] - - if len(valid_col) == 0: - edges = np.array([], dtype=np.float64) - binned_col[:] = MISSING_BIN - else: - edges = np.nanpercentile(valid_col, percentiles) - edges = np.unique(edges) - # searchsorted is what digitize calls internally, minus overhead - bin_idx = np.searchsorted(edges, valid_col, side='right') - np.clip(bin_idx, 0, len(edges) - 1, out=bin_idx) - binned_col[~nan_mask] = bin_idx.astype(np.uint8) - binned_col[nan_mask] = MISSING_BIN - else: - edges = np.percentile(col, percentiles) - edges = np.unique(edges) - bin_idx = np.searchsorted(edges, col, side='right') - np.clip(bin_idx, 0, len(edges) - 1, out=bin_idx) - binned_col = bin_idx.astype(np.uint8) - - return binned_col, edges, has_nan, False, None, 0 - - -def _bin_categorical_feature( - col: NDArray, -) -> tuple[NDArray[np.uint8], NDArray[np.float64], bool, bool, dict, int]: - """Bin a categorical feature by mapping unique values to bin indices. - - Each unique value maps to a bin index 0, 1, 2, ... - NaN/None values map to MISSING_BIN (255). - - Returns: - binned_col, bin_edges (empty), has_nan, is_categorical, category_map, n_categories - """ - binned_col = np.zeros(len(col), dtype=np.uint8) - - # Handle different types (numeric with NaN, object with None/NaN) - try: - # Try numeric - NaN is missing - col_float = col.astype(np.float64) - nan_mask = np.isnan(col_float) - except (ValueError, TypeError): - # Object dtype - check for None, NaN, 'nan', etc. - nan_mask = np.array([ - v is None or (isinstance(v, float) and np.isnan(v)) or v == 'nan' or v == '' - for v in col - ], dtype=np.bool_) - - has_nan = bool(np.any(nan_mask)) - - # Get unique non-missing values - valid_values = col[~nan_mask] if has_nan else col - - unique_vals = np.unique(valid_values) - n_categories = len(unique_vals) - - if n_categories > 254: - raise ValueError( - f"Categorical feature has {n_categories} unique values, max is 254 " - "(bin 255 reserved for missing values)" - ) - - # Create mapping: value -> bin index (preserve original types so - # string/object categories work, not just numeric ones) - category_map = {} - for i, v in enumerate(unique_vals): - item = v.item() if hasattr(v, 'item') else v - category_map[item] = i - - # Encode values - for i, v in enumerate(col): - if nan_mask[i]: - binned_col[i] = MISSING_BIN - else: - item = v.item() if hasattr(v, 'item') else v - binned_col[i] = category_map[item] - - # Empty edges for categorical (not used in splits) - edges = np.array([], dtype=np.float64) - - return binned_col, edges, has_nan, True, category_map, n_categories - - -def _quantile_bin( - X: NDArray[np.floating], - n_bins: int, -) -> tuple[NDArray[np.uint8], list[NDArray[np.float64]], NDArray[np.bool_]]: - """Bin features using quantiles (parallelized across features). - - Handles missing values (NaN) by encoding them as MISSING_BIN (255). - Bin edges are computed only on non-missing values. - - Note: This is a legacy function. Use _bin_features for new code. - - Args: - X: Input data, shape (n_samples, n_features), may contain NaN - n_bins: Number of bins (max 255, leaving 255 for missing) - - Returns: - binned: Binned data, shape (n_samples, n_features), uint8 - NaN values are encoded as 255 - bin_edges: List of bin edges per feature - has_missing: Boolean array (n_features,) indicating which features have NaN - """ - # Use new function with no categorical features - binned, bin_edges, has_missing, _, _, _ = _bin_features(X, n_bins, set()) - return binned, bin_edges, has_missing - - -def as_numba_array(arr): - """Convert GPU array to Numba device array (zero-copy where possible). - - Supports PyTorch, JAX, CuPy arrays via __cuda_array_interface__. - - Args: - arr: Array with __cuda_array_interface__ or numpy array - - Returns: - Numba device array (CUDA) or numpy array (CPU) - """ - # CUDA arrays (PyTorch .cuda(), JAX GPU, CuPy) - if hasattr(arr, '__cuda_array_interface__'): - if is_cuda(): - from ._backends._cuda import as_cuda_array - return as_cuda_array(arr) - else: - raise RuntimeError( - "Received CUDA array but CUDA backend not available. " - "Call arr.cpu() first or set OPENBOOST_BACKEND=cpu" - ) - - # CPU arrays - if hasattr(arr, '__array_interface__'): - return np.asarray(arr) - - # Already numpy - if isinstance(arr, np.ndarray): - return arr - - raise TypeError( - f"Cannot convert {type(arr).__name__} to Numba array. " - "Expected numpy, PyTorch, JAX, or CuPy array." - ) - - -def ensure_contiguous_float32(arr) -> np.ndarray: - """Ensure array is contiguous float32 (for grad/hess).""" - arr = _to_numpy(arr) if not isinstance(arr, np.ndarray) else arr - if arr.dtype != np.float32: - arr = arr.astype(np.float32) - if not arr.flags['C_CONTIGUOUS']: - arr = np.ascontiguousarray(arr) - return arr diff --git a/src/openboost/_backends/__init__.py b/src/openboost/_backends/__init__.py deleted file mode 100644 index 737f758..0000000 --- a/src/openboost/_backends/__init__.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Backend detection and dispatch for OpenBoost.""" - -from __future__ import annotations - -import os -import threading -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from typing import Literal - -# Backend state -_BACKEND: Literal["cuda", "cpu"] | None = None -_BACKEND_LOCK = threading.Lock() - - -def get_backend() -> Literal["cuda", "cpu"]: - """Get the current compute backend. - - Returns: - "cuda" if NVIDIA GPU is available, "cpu" otherwise. - """ - global _BACKEND - - with _BACKEND_LOCK: - if _BACKEND is not None: - return _BACKEND - - # Allow override via environment variable - env_backend = os.environ.get("OPENBOOST_BACKEND", "").lower() - if env_backend in ("cuda", "cpu"): - _BACKEND = env_backend - return _BACKEND - - # Auto-detect CUDA - _BACKEND = "cuda" if _cuda_available() else "cpu" - return _BACKEND - - -def _cuda_available() -> bool: - """Check if CUDA is available via Numba.""" - try: - from numba import cuda - return cuda.is_available() - except Exception: - return False - - -def set_backend(backend: Literal["cuda", "cpu"]) -> None: - """Force a specific backend. - - Thread-safe: uses a lock to prevent concurrent modification. - - Args: - backend: "cuda" or "cpu" - - Raises: - ValueError: If backend is not "cuda" or "cpu" - RuntimeError: If CUDA is requested but not available - """ - global _BACKEND - - if backend not in ("cuda", "cpu"): - raise ValueError(f"backend must be 'cuda' or 'cpu', got {backend!r}") - - if backend == "cuda" and not _cuda_available(): - raise RuntimeError("CUDA backend requested but CUDA is not available") - - with _BACKEND_LOCK: - _BACKEND = backend - - -def is_cuda() -> bool: - """Check if using CUDA backend.""" - return get_backend() == "cuda" - - -def is_cpu() -> bool: - """Check if using CPU backend.""" - return get_backend() == "cpu" - - -def clear_tree_workspace_cache() -> None: - """Free cached GPU tree-building workspace arrays (no-op on CPU). - - Call this between training runs that use different data shapes to avoid - holding onto stale GPU allocations. Safe to call on CPU-only installs, - where it does nothing. - """ - if get_backend() != "cuda": - return - from ._cuda import clear_tree_workspace_cache as _clear - _clear() - - -class backend_context: - """Context manager for temporarily switching the compute backend. - - Restores the previous backend on exit, even if an exception occurs. If no - backend had been resolved yet on entry, exit restores the unresolved state - so the backend is auto-detected again on next use. - - Note: - This mutates the **process-global** backend, not a thread-local one. - It is therefore not safe to use concurrently from multiple threads — - one thread's context will affect every other thread. - - Example:: - - with backend_context('cpu'): - model.fit(X, y) # Forces CPU - # Original backend is restored here - """ - - def __init__(self, backend: Literal["cuda", "cpu"]) -> None: - self._backend = backend - self._previous: Literal["cuda", "cpu"] | None = None - - def __enter__(self) -> None: - with _BACKEND_LOCK: - self._previous = _BACKEND - set_backend(self._backend) - - def __exit__(self, *exc: object) -> None: - global _BACKEND - # Restore by direct assignment (set_backend only writes the global and - # rejects None; direct assignment also lets us restore the - # "unresolved -> auto-detect" state when _previous is None). - with _BACKEND_LOCK: - _BACKEND = self._previous - diff --git a/src/openboost/_backends/_cpu.py b/src/openboost/_backends/_cpu.py deleted file mode 100644 index a26d64d..0000000 --- a/src/openboost/_backends/_cpu.py +++ /dev/null @@ -1,781 +0,0 @@ -"""CPU backend implementations using Numba JIT.""" - -from __future__ import annotations - -import numpy as np -from numba import jit, prange - -# ============================================================================= -# Histogram Functions -# ============================================================================= - -@jit(nopython=True, parallel=True, cache=True) -def _build_histogram_cpu( - binned: np.ndarray, # (n_features, n_samples) uint8 - grad: np.ndarray, # (n_samples,) float32 - hess: np.ndarray, # (n_samples,) float32 - hist_grad: np.ndarray, # (n_features, 256) float32 - hist_hess: np.ndarray, # (n_features, 256) float32 -): - """Build gradient and hessian histograms for all features (CPU). - - Phase 3.3: Use float32 to match GPU performance characteristics. - """ - n_features = binned.shape[0] - n_samples = binned.shape[1] - - # Process features in parallel - for f in prange(n_features): - # Local histograms (float32 to match GPU) - local_grad = np.zeros(256, dtype=np.float32) - local_hess = np.zeros(256, dtype=np.float32) - - for i in range(n_samples): - bin_idx = binned[f, i] - local_grad[bin_idx] += grad[i] - local_hess[bin_idx] += hess[i] - - hist_grad[f, :] = local_grad - hist_hess[f, :] = local_hess - - -def build_histogram_cpu( - binned: np.ndarray, - grad: np.ndarray, - hess: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: - """Build histograms on CPU. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples), uint8 - grad: Gradient vector, shape (n_samples,), float32 - hess: Hessian vector, shape (n_samples,), float32 - - Returns: - hist_grad: Gradient histogram, shape (n_features, 256), float32 - hist_hess: Hessian histogram, shape (n_features, 256), float32 - """ - n_features = binned.shape[0] - - # Phase 3.3: float32 to match GPU - hist_grad = np.zeros((n_features, 256), dtype=np.float32) - hist_hess = np.zeros((n_features, 256), dtype=np.float32) - - _build_histogram_cpu(binned, grad, hess, hist_grad, hist_hess) - - return hist_grad, hist_hess - - -# ============================================================================= -# Split Finding -# ============================================================================= - -@jit(nopython=True, parallel=True, cache=True) -def _find_best_split_all_features( - hist_grad: np.ndarray, # (n_features, 256) float64 - hist_hess: np.ndarray, # (n_features, 256) float64 - total_grad: float, - total_hess: float, - reg_lambda: float, - min_child_weight: float, - best_gains: np.ndarray, # (n_features,) float64 - best_bins: np.ndarray, # (n_features,) int32 -): - """Find best split for each feature in parallel.""" - n_features = hist_grad.shape[0] - - parent_gain = (total_grad * total_grad) / (total_hess + reg_lambda) - - for f in prange(n_features): - best_gain = -1e10 - best_bin = -1 - - left_grad = 0.0 - left_hess = 0.0 - - for bin_idx in range(255): - left_grad += hist_grad[f, bin_idx] - left_hess += hist_hess[f, bin_idx] - - right_grad = total_grad - left_grad - right_hess = total_hess - left_hess - - if left_hess < min_child_weight or right_hess < min_child_weight: - continue - - left_score = (left_grad * left_grad) / (left_hess + reg_lambda) - right_score = (right_grad * right_grad) / (right_hess + reg_lambda) - gain = left_score + right_score - parent_gain - - if gain > best_gain: - best_gain = gain - best_bin = bin_idx - - best_gains[f] = best_gain - best_bins[f] = best_bin - - -def find_best_split_cpu( - hist_grad: np.ndarray, - hist_hess: np.ndarray, - total_grad: float, - total_hess: float, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, -) -> tuple[int, int, float]: - """Find the best split across all features (CPU). - - Returns: - best_feature: Index of best feature (-1 if no valid split) - best_bin: Bin threshold for split - best_gain: Gain from the split - """ - n_features = hist_grad.shape[0] - - # Guard for zero features - if n_features == 0: - return -1, -1, 0.0 - - # Phase 3.3: Keep float64 for gain comparison precision on CPU - # (CPU has no float64 penalty, and this is small data) - best_gains = np.full(n_features, -1e10, dtype=np.float64) - best_bins = np.full(n_features, -1, dtype=np.int32) - - _find_best_split_all_features( - hist_grad, hist_hess, - total_grad, total_hess, - reg_lambda, min_child_weight, - best_gains, best_bins, - ) - - best_feature = int(np.argmax(best_gains)) - best_gain = float(best_gains[best_feature]) - best_bin = int(best_bins[best_feature]) - - if best_gain <= 0 or best_bin < 0: - return -1, -1, 0.0 - - return best_feature, best_bin, best_gain - - -# ============================================================================= -# Phase 14: Missing Value Support -# ============================================================================= - -MISSING_BIN = 255 # Reserved bin for missing values - - -@jit(nopython=True, cache=True) -def _compute_gain( - left_grad: float, - left_hess: float, - right_grad: float, - right_hess: float, - reg_lambda: float, -) -> float: - """Compute split gain.""" - total_grad = left_grad + right_grad - total_hess = left_hess + right_hess - parent_gain = (total_grad * total_grad) / (total_hess + reg_lambda) - left_gain = (left_grad * left_grad) / (left_hess + reg_lambda) - right_gain = (right_grad * right_grad) / (right_hess + reg_lambda) - return left_gain + right_gain - parent_gain - - -@jit(nopython=True, parallel=True, cache=True) -def _find_best_split_with_missing_all_features( - hist_grad: np.ndarray, # (n_features, 256) float64 - hist_hess: np.ndarray, # (n_features, 256) float64 - total_grad: float, - total_hess: float, - reg_lambda: float, - min_child_weight: float, - has_missing: np.ndarray, # (n_features,) bool - best_gains: np.ndarray, # (n_features,) float64 - best_bins: np.ndarray, # (n_features,) int32 - best_missing_left: np.ndarray, # (n_features,) bool -): - """Find best split for each feature considering missing values.""" - n_features = hist_grad.shape[0] - - for f in prange(n_features): - best_gain = -1e10 - best_bin = -1 - best_miss_left = True - - # Extract missing statistics for this feature - miss_grad = hist_grad[f, MISSING_BIN] - miss_hess = hist_hess[f, MISSING_BIN] - feature_has_missing = has_missing[f] - - # Total for non-missing values - nonmiss_total_grad = total_grad - miss_grad if feature_has_missing else total_grad - nonmiss_total_hess = total_hess - miss_hess if feature_has_missing else total_hess - - left_grad = 0.0 - left_hess = 0.0 - - # Iterate through bins 0-254 (not 255 which is missing) - for bin_idx in range(255): - left_grad += hist_grad[f, bin_idx] - left_hess += hist_hess[f, bin_idx] - - right_grad = nonmiss_total_grad - left_grad - right_hess = nonmiss_total_hess - left_hess - - if feature_has_missing and (miss_grad != 0.0 or miss_hess != 0.0): - # Try missing goes LEFT - left_g_miss = left_grad + miss_grad - left_h_miss = left_hess + miss_hess - - if left_h_miss >= min_child_weight and right_hess >= min_child_weight: - gain_miss_left = _compute_gain( - left_g_miss, left_h_miss, - right_grad, right_hess, - reg_lambda - ) - if gain_miss_left > best_gain: - best_gain = gain_miss_left - best_bin = bin_idx - best_miss_left = True - - # Try missing goes RIGHT - right_g_miss = right_grad + miss_grad - right_h_miss = right_hess + miss_hess - - if left_hess >= min_child_weight and right_h_miss >= min_child_weight: - gain_miss_right = _compute_gain( - left_grad, left_hess, - right_g_miss, right_h_miss, - reg_lambda - ) - if gain_miss_right > best_gain: - best_gain = gain_miss_right - best_bin = bin_idx - best_miss_left = False - else: - # No missing values - standard split - if left_hess >= min_child_weight and right_hess >= min_child_weight: - gain = _compute_gain(left_grad, left_hess, right_grad, right_hess, reg_lambda) - if gain > best_gain: - best_gain = gain - best_bin = bin_idx - best_miss_left = True # Default direction - - best_gains[f] = best_gain - best_bins[f] = best_bin - best_missing_left[f] = best_miss_left - - -def find_best_split_with_missing_cpu( - hist_grad: np.ndarray, - hist_hess: np.ndarray, - total_grad: float, - total_hess: float, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - has_missing: np.ndarray | None = None, -) -> tuple[int, int, float, bool]: - """Find the best split considering missing values (CPU). - - Returns: - best_feature: Index of best feature (-1 if no valid split) - best_bin: Bin threshold for split - best_gain: Gain from the split - missing_go_left: Whether missing values should go left - """ - n_features = hist_grad.shape[0] - - # If has_missing not provided, check for non-zero bin 255 - if has_missing is None: - has_missing = np.zeros(n_features, dtype=np.bool_) - for f in range(n_features): - if hist_grad[f, MISSING_BIN] != 0 or hist_hess[f, MISSING_BIN] != 0: - has_missing[f] = True - - # NOTE: CPU split-finding uses float64 for higher precision. - # GPU uses float32 (see _backends/_cuda.py), which can produce - # slightly different "best split" results, affecting reproducibility. - best_gains = np.full(n_features, -1e10, dtype=np.float64) - best_bins = np.full(n_features, -1, dtype=np.int32) - best_missing_left = np.ones(n_features, dtype=np.bool_) - - _find_best_split_with_missing_all_features( - hist_grad.astype(np.float64), - hist_hess.astype(np.float64), - total_grad, total_hess, - reg_lambda, min_child_weight, - has_missing, - best_gains, best_bins, best_missing_left, - ) - - best_feature = int(np.argmax(best_gains)) - best_gain = float(best_gains[best_feature]) - best_bin = int(best_bins[best_feature]) - best_miss_left = bool(best_missing_left[best_feature]) - - if best_gain <= 0 or best_bin < 0: - return -1, -1, 0.0, True - - return best_feature, best_bin, best_gain, best_miss_left - - -# ============================================================================= -# Phase 14.3: Categorical Split Finding -# ============================================================================= - -def find_best_split_categorical_cpu( - hist_grad: np.ndarray, - hist_hess: np.ndarray, - total_grad: float, - total_hess: float, - reg_lambda: float, - min_child_weight: float, - has_missing: np.ndarray, - is_categorical: np.ndarray, - n_categories: np.ndarray, -) -> tuple[int, int, float, bool, bool, int, int]: - """Find best split with categorical and missing value support (CPU). - - For numeric features: ordinal splits (value <= threshold) - For categorical features: Fisher-based optimal category ordering - - Returns: - best_feature: Feature index (-1 if no valid split) - best_threshold: Bin threshold (ordinal) or category split point - best_gain: Gain from the split - missing_go_left: Direction for missing values - is_cat: Whether the best split is categorical - cat_bitset: Bitmask for categories going left (for categorical) - cat_threshold: Split point in sorted category order - """ - n_features = hist_grad.shape[0] - - best_feature = -1 - best_threshold = -1 - best_gain = -1e10 - best_missing_left = True - best_is_cat = False - best_cat_bitset = 0 - best_cat_threshold = -1 - - for f in range(n_features): - miss_grad = hist_grad[f, MISSING_BIN] - miss_hess = hist_hess[f, MISSING_BIN] - has_miss = has_missing[f] - - if is_categorical[f]: - # Categorical split using Fisher's optimal ordering - n_cats = int(n_categories[f]) - if n_cats < 2: - continue - - gain, cat_threshold, cat_bitset, miss_left = _find_best_categorical_split( - hist_grad[f], hist_hess[f], - n_cats, miss_grad, miss_hess, has_miss, - reg_lambda, min_child_weight, - ) - - if gain > best_gain: - best_gain = gain - best_feature = f - best_threshold = cat_threshold - best_missing_left = miss_left - best_is_cat = True - best_cat_bitset = cat_bitset - best_cat_threshold = cat_threshold - else: - # Numeric split (ordinal) - gain, threshold, miss_left = _find_best_numeric_split( - hist_grad[f], hist_hess[f], - total_grad, total_hess, - miss_grad, miss_hess, has_miss, - reg_lambda, min_child_weight, - ) - - if gain > best_gain: - best_gain = gain - best_feature = f - best_threshold = threshold - best_missing_left = miss_left - best_is_cat = False - best_cat_bitset = 0 - best_cat_threshold = -1 - - if best_gain <= 0 or best_feature < 0: - return -1, -1, 0.0, True, False, 0, -1 - - return (best_feature, best_threshold, best_gain, best_missing_left, - best_is_cat, best_cat_bitset, best_cat_threshold) - - -def _find_best_numeric_split( - hist_grad_f: np.ndarray, # (256,) - hist_hess_f: np.ndarray, - total_grad: float, - total_hess: float, - miss_grad: float, - miss_hess: float, - has_miss: bool, - reg_lambda: float, - min_child_weight: float, -) -> tuple[float, int, bool]: - """Find best numeric (ordinal) split for a single feature. - - Returns: (gain, threshold, missing_go_left) - """ - best_gain = -1e10 - best_threshold = -1 - best_miss_left = True - - # Non-missing totals - nonmiss_total_grad = total_grad - miss_grad if has_miss else total_grad - nonmiss_total_hess = total_hess - miss_hess if has_miss else total_hess - - left_grad = 0.0 - left_hess = 0.0 - - for bin_idx in range(255): - left_grad += hist_grad_f[bin_idx] - left_hess += hist_hess_f[bin_idx] - - right_grad = nonmiss_total_grad - left_grad - right_hess = nonmiss_total_hess - left_hess - - if has_miss and (miss_grad != 0.0 or miss_hess != 0.0): - # Try missing LEFT - left_g_miss = left_grad + miss_grad - left_h_miss = left_hess + miss_hess - - if left_h_miss >= min_child_weight and right_hess >= min_child_weight: - gain = _compute_gain(left_g_miss, left_h_miss, right_grad, right_hess, reg_lambda) - if gain > best_gain: - best_gain = gain - best_threshold = bin_idx - best_miss_left = True - - # Try missing RIGHT - right_g_miss = right_grad + miss_grad - right_h_miss = right_hess + miss_hess - - if left_hess >= min_child_weight and right_h_miss >= min_child_weight: - gain = _compute_gain(left_grad, left_hess, right_g_miss, right_h_miss, reg_lambda) - if gain > best_gain: - best_gain = gain - best_threshold = bin_idx - best_miss_left = False - else: - if left_hess >= min_child_weight and right_hess >= min_child_weight: - gain = _compute_gain(left_grad, left_hess, right_grad, right_hess, reg_lambda) - if gain > best_gain: - best_gain = gain - best_threshold = bin_idx - best_miss_left = True - - return best_gain, best_threshold, best_miss_left - - -def _find_best_categorical_split( - hist_grad_f: np.ndarray, # (256,) - hist_hess_f: np.ndarray, - n_categories: int, - miss_grad: float, - miss_hess: float, - has_miss: bool, - reg_lambda: float, - min_child_weight: float, -) -> tuple[float, int, int, bool]: - """Find best categorical split using Fisher's optimal ordering. - - Fisher's method: Sort categories by gradient/hessian ratio, - then find best split point in sorted order. - - Returns: (gain, cat_threshold, cat_bitset, missing_go_left) - """ - # Compute ordering score for each category: -G / (H + lambda) - eps = 1e-10 - scores = np.zeros(n_categories, dtype=np.float64) - cat_grads = np.zeros(n_categories, dtype=np.float64) - cat_hess = np.zeros(n_categories, dtype=np.float64) - - for cat in range(n_categories): - cat_grads[cat] = hist_grad_f[cat] - cat_hess[cat] = hist_hess_f[cat] - if cat_hess[cat] > eps: - scores[cat] = -cat_grads[cat] / (cat_hess[cat] + reg_lambda) - else: - scores[cat] = 0.0 - - # Sort categories by score - sorted_cats = np.argsort(scores) - - # Total gradient/hessian for this feature (excluding missing) - total_g = float(np.sum(cat_grads)) - total_h = float(np.sum(cat_hess)) - - best_gain = -1e10 - best_split = 0 - best_miss_left = True - - # Find best split point in sorted order - left_g = 0.0 - left_h = 0.0 - - for i in range(n_categories - 1): - cat = sorted_cats[i] - left_g += cat_grads[cat] - left_h += cat_hess[cat] - - right_g = total_g - left_g - right_h = total_h - left_h - - if has_miss and (miss_grad != 0.0 or miss_hess != 0.0): - # Try missing LEFT - if left_h + miss_hess >= min_child_weight and right_h >= min_child_weight: - gain = _compute_gain(left_g + miss_grad, left_h + miss_hess, - right_g, right_h, reg_lambda) - if gain > best_gain: - best_gain = gain - best_split = i + 1 - best_miss_left = True - - # Try missing RIGHT - if left_h >= min_child_weight and right_h + miss_hess >= min_child_weight: - gain = _compute_gain(left_g, left_h, - right_g + miss_grad, right_h + miss_hess, reg_lambda) - if gain > best_gain: - best_gain = gain - best_split = i + 1 - best_miss_left = False - else: - if left_h >= min_child_weight and right_h >= min_child_weight: - gain = _compute_gain(left_g, left_h, right_g, right_h, reg_lambda) - if gain > best_gain: - best_gain = gain - best_split = i + 1 - best_miss_left = True - - # Build bitmask: categories in sorted order before split_point go left. - # cat_bitset is 64-bit, so category bins >= 64 cannot be represented; they - # are never placed in the left set here and are always routed right at - # partition/predict time (kept consistent across CPU and GPU). - cat_bitset = 0 - for i in range(best_split): - cat = sorted_cats[i] - if cat < 64: - cat_bitset |= (1 << cat) - - return best_gain, best_split, cat_bitset, best_miss_left - - -# ============================================================================= -# Prediction -# ============================================================================= - -@jit(nopython=True, parallel=True, cache=True) -def _predict_cpu( - binned: np.ndarray, # (n_features, n_samples) uint8 - tree_features: np.ndarray, # (n_nodes,) int32 - tree_thresholds: np.ndarray, # (n_nodes,) uint8 - tree_values: np.ndarray, # (n_nodes,) float32 - tree_left: np.ndarray, # (n_nodes,) int32 - tree_right: np.ndarray, # (n_nodes,) int32 - predictions: np.ndarray, # (n_samples,) float32 -): - """Predict using tree structure (CPU).""" - n_samples = binned.shape[1] - - for i in prange(n_samples): - node = 0 - while tree_left[node] != -1: - feature = tree_features[node] - threshold = tree_thresholds[node] - bin_value = binned[feature, i] - - node = tree_left[node] if bin_value <= threshold else tree_right[node] - - predictions[i] = tree_values[node] - - -def predict_cpu( - binned: np.ndarray, - tree_features: np.ndarray, - tree_thresholds: np.ndarray, - tree_values: np.ndarray, - tree_left: np.ndarray, - tree_right: np.ndarray, - tree_missing_left: np.ndarray | None = None, -) -> np.ndarray: - """Predict using a tree on CPU. - - Phase 14: Added support for missing value routing. - - Args: - binned: Binned features (n_features, n_samples) - tree_features: Feature index for each node - tree_thresholds: Threshold for each node - tree_values: Leaf values - tree_left: Left child indices - tree_right: Right child indices - tree_missing_left: Whether missing goes left for each node (Phase 14) - - Returns: - predictions: Shape (n_samples,), float32 - """ - n_samples = binned.shape[1] - predictions = np.empty(n_samples, dtype=np.float32) - - if tree_missing_left is not None: - _predict_cpu_with_missing( - binned, tree_features, tree_thresholds, tree_values, - tree_left, tree_right, tree_missing_left, predictions - ) - else: - _predict_cpu( - binned, tree_features, tree_thresholds, tree_values, - tree_left, tree_right, predictions - ) - - return predictions - - -# ============================================================================= -# Phase 14: Prediction with Missing Value Support -# ============================================================================= - -@jit(nopython=True, parallel=True, cache=True) -def _predict_cpu_with_missing( - binned: np.ndarray, # (n_features, n_samples) uint8 - tree_features: np.ndarray, # (n_nodes,) int32 - tree_thresholds: np.ndarray, # (n_nodes,) uint8 - tree_values: np.ndarray, # (n_nodes,) float32 - tree_left: np.ndarray, # (n_nodes,) int32 - tree_right: np.ndarray, # (n_nodes,) int32 - tree_missing_left: np.ndarray, # (n_nodes,) bool - predictions: np.ndarray, # (n_samples,) float32 -): - """Predict using tree structure with missing value handling (CPU). - - Missing values (bin 255) are routed according to the learned direction - stored in tree_missing_left. - """ - n_samples = binned.shape[1] - - for i in prange(n_samples): - node = 0 - while tree_left[node] != -1: - feature = tree_features[node] - threshold = tree_thresholds[node] - bin_value = binned[feature, i] - - # Check for missing value - if bin_value == MISSING_BIN: - # Use learned direction - node = tree_left[node] if tree_missing_left[node] else tree_right[node] - elif bin_value <= threshold: - node = tree_left[node] - else: - node = tree_right[node] - - predictions[i] = tree_values[node] - - -# ============================================================================= -# Phase 14.4: Prediction with Categorical Split Support (CPU) -# ============================================================================= - -@jit(nopython=True, parallel=True, cache=True) -def _predict_cpu_with_categorical( - binned: np.ndarray, # (n_features, n_samples) uint8 - tree_features: np.ndarray, # (n_nodes,) int32 - tree_thresholds: np.ndarray, # (n_nodes,) uint8 - tree_values: np.ndarray, # (n_nodes,) float32 - tree_left: np.ndarray, # (n_nodes,) int32 - tree_right: np.ndarray, # (n_nodes,) int32 - tree_missing_left: np.ndarray, # (n_nodes,) bool - is_categorical_split: np.ndarray, # (n_nodes,) bool - cat_bitsets: np.ndarray, # (n_nodes,) int64 - predictions: np.ndarray, # (n_samples,) float32 -): - """Predict using tree structure with categorical and missing value handling (CPU). - - For categorical splits, go left if (1 << bin_value) & cat_bitset != 0. - For numeric splits, go left if bin_value <= threshold. - Missing values (bin 255) are routed according to tree_missing_left. - """ - n_samples = binned.shape[1] - - for i in prange(n_samples): - node = 0 - while tree_left[node] != -1: - feature = tree_features[node] - bin_value = binned[feature, i] - - # Check for missing value - if bin_value == MISSING_BIN: - node = tree_left[node] if tree_missing_left[node] else tree_right[node] - elif is_categorical_split[node]: - # Categorical split: use bitmask. Bins >= 64 are not - # representable in the 64-bit bitset and always go right. - bitset = cat_bitsets[node] - goes_left = False - if bin_value < 64: - goes_left = ((np.int64(1) << bin_value) & bitset) != 0 - node = tree_left[node] if goes_left else tree_right[node] - else: - # Numeric split: use threshold - threshold = tree_thresholds[node] - node = tree_left[node] if bin_value <= threshold else tree_right[node] - - predictions[i] = tree_values[node] - - -def predict_with_categorical_cpu( - binned: np.ndarray, - tree_features: np.ndarray, - tree_thresholds: np.ndarray, - tree_values: np.ndarray, - tree_left: np.ndarray, - tree_right: np.ndarray, - tree_missing_left: np.ndarray | None = None, - is_categorical_split: np.ndarray | None = None, - cat_bitsets: np.ndarray | None = None, -) -> np.ndarray: - """Predict using a tree with categorical support on CPU. - - Phase 14.4: CPU prediction supporting categorical splits. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples) - tree_features: Feature index for each node - tree_thresholds: Threshold for each node - tree_values: Leaf values - tree_left: Left child indices - tree_right: Right child indices - tree_missing_left: Whether missing goes left for each node - is_categorical_split: Whether each node is a categorical split - cat_bitsets: Bitmasks for categorical splits - - Returns: - predictions: Shape (n_samples,), float32 - """ - n_samples = binned.shape[1] - n_nodes = tree_features.shape[0] - predictions = np.empty(n_samples, dtype=np.float32) - - # Prepare default arrays if not provided - if tree_missing_left is None: - tree_missing_left = np.ones(n_nodes, dtype=np.bool_) - if is_categorical_split is None: - is_categorical_split = np.zeros(n_nodes, dtype=np.bool_) - if cat_bitsets is None: - cat_bitsets = np.zeros(n_nodes, dtype=np.int64) - - _predict_cpu_with_categorical( - binned, tree_features, tree_thresholds, tree_values, - tree_left, tree_right, tree_missing_left, - is_categorical_split, cat_bitsets, predictions - ) - - return predictions - diff --git a/src/openboost/_backends/_cuda.py b/src/openboost/_backends/_cuda.py deleted file mode 100644 index 5ca1d42..0000000 --- a/src/openboost/_backends/_cuda.py +++ /dev/null @@ -1,3714 +0,0 @@ -"""CUDA backend implementations using Numba CUDA. - -Phase 3.3: All histogram/gradient computations use float32 for 2x GPU throughput. -This matches XGBoost/LightGBM defaults. -""" - -from __future__ import annotations - -import math -import time as _time -from typing import TYPE_CHECKING - -import numpy as np -from numba import cuda, float32, float64, int32, int64, uint8 - -if TYPE_CHECKING: - from numba.cuda.cudadrv.devicearray import DeviceNDArray - - -# ============================================================================= -# Histogram Kernel (Phase 3.3: float32) -# ============================================================================= - -@cuda.jit -def _histogram_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - grad: DeviceNDArray, # (n_samples,) float32 - hess: DeviceNDArray, # (n_samples,) float32 - hist_grad: DeviceNDArray, # (n_features, 256) float32 - hist_hess: DeviceNDArray, # (n_features, 256) float32 -): - """Build gradient and hessian histograms for all features. - - Each block handles one feature. Threads cooperatively bin samples. - Uses shared memory for local histogram, then atomic add to global. - - Thread layout: 1D grid, 1D blocks - - blockIdx.x = feature index - - threadIdx.x = thread within block - """ - feature_idx = cuda.blockIdx.x - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory for local histogram (256 bins × 2 values) - # Phase 3.3: Use float32 for 2x throughput (matches XGBoost/LightGBM) - local_grad = cuda.shared.array(256, dtype=float32) - local_hess = cuda.shared.array(256, dtype=float32) - - # Initialize shared memory - for i in range(thread_idx, 256, block_size): - local_grad[i] = float32(0.0) - local_hess[i] = float32(0.0) - cuda.syncthreads() - - # Accumulate into shared memory - for sample_idx in range(thread_idx, n_samples, block_size): - bin_idx = binned[feature_idx, sample_idx] - g = grad[sample_idx] - h = hess[sample_idx] - cuda.atomic.add(local_grad, int32(bin_idx), g) - cuda.atomic.add(local_hess, int32(bin_idx), h) - cuda.syncthreads() - - # Write to global memory - for i in range(thread_idx, 256, block_size): - hist_grad[feature_idx, i] = local_grad[i] - hist_hess[feature_idx, i] = local_hess[i] - - -def build_histogram_cuda( - binned: DeviceNDArray, - grad: DeviceNDArray, - hess: DeviceNDArray, -) -> tuple[DeviceNDArray, DeviceNDArray]: - """Build histograms on GPU. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples), uint8 - grad: Gradient vector, shape (n_samples,), float32 - hess: Hessian vector, shape (n_samples,), float32 - - Returns: - hist_grad: Gradient histogram, shape (n_features, 256), float32 - hist_hess: Hessian histogram, shape (n_features, 256), float32 - """ - n_features = binned.shape[0] - - # Allocate output (Phase 3.3: float32) - hist_grad = cuda.device_array((n_features, 256), dtype=np.float32) - hist_hess = cuda.device_array((n_features, 256), dtype=np.float32) - - # Launch: one block per feature, 256 threads per block - threads_per_block = 256 - blocks = n_features - - _histogram_kernel[blocks, threads_per_block]( - binned, grad, hess, hist_grad, hist_hess - ) - - return hist_grad, hist_hess - - -# ============================================================================= -# Histogram Subtraction (Phase 3, updated 3.3: float32) -# ============================================================================= - -@cuda.jit -def _subtract_2d_kernel( - parent: DeviceNDArray, # (n_features, 256) float32 - child: DeviceNDArray, # (n_features, 256) float32 - result: DeviceNDArray, # (n_features, 256) float32 -): - """Subtract child from parent histogram: result = parent - child. - - Thread layout: 1D grid covering all elements. - """ - idx = cuda.grid(1) - n_features = parent.shape[0] - total_elements = n_features * 256 - - if idx < total_elements: - feature = idx // 256 - bin_idx = idx % 256 - result[feature, bin_idx] = parent[feature, bin_idx] - child[feature, bin_idx] - - -def subtract_histograms_cuda( - parent_grad: DeviceNDArray, - parent_hess: DeviceNDArray, - child_grad: DeviceNDArray, - child_hess: DeviceNDArray, -) -> tuple[DeviceNDArray, DeviceNDArray]: - """Compute sibling histogram via subtraction on GPU. - - sibling = parent - child - - Args: - parent_grad, parent_hess: Parent histograms, shape (n_features, 256) - child_grad, child_hess: Child histograms, shape (n_features, 256) - - Returns: - sibling_grad, sibling_hess: Sibling histograms, shape (n_features, 256) - """ - n_features = parent_grad.shape[0] - total_elements = n_features * 256 - - # Phase 3.3: float32 - sibling_grad = cuda.device_array((n_features, 256), dtype=np.float32) - sibling_hess = cuda.device_array((n_features, 256), dtype=np.float32) - - threads = 256 - blocks = math.ceil(total_elements / threads) - - _subtract_2d_kernel[blocks, threads](parent_grad, child_grad, sibling_grad) - _subtract_2d_kernel[blocks, threads](parent_hess, child_hess, sibling_hess) - - return sibling_grad, sibling_hess - - -# ============================================================================= -# Reduction Kernels (Phase 2) -# ============================================================================= - -@cuda.jit -def _reduce_sum_indexed_kernel( - arr: DeviceNDArray, # (n_total,) float32 - full array - sample_indices: DeviceNDArray, # (n_samples,) int32 - indices to sum - n_samples: int32, - partial_sums: DeviceNDArray, # (n_blocks,) float64 - partial results -): - """Parallel reduction for indexed subset sums. - - Each block computes a partial sum using shared memory reduction. - Final sum requires a second pass or CPU aggregation of small array. - - Thread layout: 1D grid, 1D blocks - - blockIdx.x = block index - - threadIdx.x = thread within block - """ - thread_idx = cuda.threadIdx.x - block_idx = cuda.blockIdx.x - block_size = cuda.blockDim.x - - # Shared memory for block-level reduction - shared = cuda.shared.array(256, dtype=float64) - - # Each thread accumulates multiple elements - local_sum = float64(0.0) - global_idx = block_idx * block_size + thread_idx - stride = block_size * cuda.gridDim.x - - while global_idx < n_samples: - sample_idx = sample_indices[global_idx] - local_sum += float64(arr[sample_idx]) - global_idx += stride - - shared[thread_idx] = local_sum - cuda.syncthreads() - - # Tree reduction in shared memory - s = block_size // 2 - while s > 0: - if thread_idx < s: - shared[thread_idx] += shared[thread_idx + s] - cuda.syncthreads() - s //= 2 - - # Thread 0 writes block result - if thread_idx == 0: - partial_sums[block_idx] = shared[0] - - -@cuda.jit -def _reduce_final_kernel( - partial_sums: DeviceNDArray, # (n_blocks,) float64 - n_blocks: int32, - result: DeviceNDArray, # (1,) float64 -): - """Final reduction of partial sums from blocks. - - Single block kernel to sum all partial results. - """ - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - shared = cuda.shared.array(256, dtype=float64) - - # Each thread sums multiple partials if needed - local_sum = float64(0.0) - idx = thread_idx - while idx < n_blocks: - local_sum += partial_sums[idx] - idx += block_size - - shared[thread_idx] = local_sum - cuda.syncthreads() - - # Tree reduction - s = block_size // 2 - while s > 0: - if thread_idx < s: - shared[thread_idx] += shared[thread_idx + s] - cuda.syncthreads() - s //= 2 - - if thread_idx == 0: - result[0] = shared[0] - - -def reduce_sum_indexed_cuda( - arr: DeviceNDArray, - sample_indices: DeviceNDArray, -) -> DeviceNDArray: - """Compute sum of arr[sample_indices] entirely on GPU. - - Args: - arr: Source array, shape (n_total,), float32 - sample_indices: Indices to sum, shape (n_samples,), int32 - - Returns: - result: Shape (1,) float64 device array containing the sum - """ - n_samples = sample_indices.shape[0] - - # Configure kernel launch - threads_per_block = 256 - n_blocks = min(256, math.ceil(n_samples / threads_per_block)) - - # Allocate partial sums - partial_sums = cuda.device_array(n_blocks, dtype=np.float64) - result = cuda.device_array(1, dtype=np.float64) - - # First pass: compute partial sums - _reduce_sum_indexed_kernel[n_blocks, threads_per_block]( - arr, sample_indices, n_samples, partial_sums - ) - - # Second pass: sum partials - _reduce_final_kernel[1, threads_per_block]( - partial_sums, n_blocks, result - ) - - return result - - -# ============================================================================= -# Argmax Kernel (Phase 2) -# ============================================================================= - -@cuda.jit -def _argmax_with_values_kernel( - gains: DeviceNDArray, # (n,) float32 - values to find max of - bins: DeviceNDArray, # (n,) int32 - associated bin values - n: int32, - result_idx: DeviceNDArray, # (1,) int32 - index of max - result_gain: DeviceNDArray, # (1,) float32 - max value - result_bin: DeviceNDArray, # (1,) int32 - bin at max index -): - """Find argmax and associated values on GPU. - - Single block kernel for small arrays (n_features typically < 1000). - Uses shared memory reduction to find maximum. - Phase 3.3: float32 for consistency. - """ - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - # Shared memory: store (value, index) pairs (Phase 3.3: float32) - shared_vals = cuda.shared.array(256, dtype=float32) - shared_idxs = cuda.shared.array(256, dtype=int32) - - # Each thread finds local max across its assigned elements - local_max = float32(-1e10) - local_idx = int32(-1) - - idx = thread_idx - while idx < n: - if gains[idx] > local_max: - local_max = gains[idx] - local_idx = idx - idx += block_size - - shared_vals[thread_idx] = local_max - shared_idxs[thread_idx] = local_idx - cuda.syncthreads() - - # Tree reduction to find global max - s = block_size // 2 - while s > 0: - if thread_idx < s and shared_vals[thread_idx + s] > shared_vals[thread_idx]: - shared_vals[thread_idx] = shared_vals[thread_idx + s] - shared_idxs[thread_idx] = shared_idxs[thread_idx + s] - cuda.syncthreads() - s //= 2 - - # Thread 0 writes result - if thread_idx == 0: - best_idx = shared_idxs[0] - result_idx[0] = best_idx - result_gain[0] = shared_vals[0] - if best_idx >= 0: - result_bin[0] = bins[best_idx] - else: - result_bin[0] = -1 - - -def argmax_with_values_cuda( - gains: DeviceNDArray, - bins: DeviceNDArray, -) -> tuple[DeviceNDArray, DeviceNDArray, DeviceNDArray]: - """Find argmax and associated values entirely on GPU. - - Args: - gains: Values to find max of, shape (n,), float32 - bins: Associated bin values, shape (n,), int32 - - Returns: - result_idx: Shape (1,) int32 - index of max - result_gain: Shape (1,) float32 - max gain value - result_bin: Shape (1,) int32 - bin value at max index - """ - n = gains.shape[0] - - result_idx = cuda.device_array(1, dtype=np.int32) - result_gain = cuda.device_array(1, dtype=np.float32) # Phase 3.3: float32 - result_bin = cuda.device_array(1, dtype=np.int32) - - # Single block, 256 threads (sufficient for typical n_features) - _argmax_with_values_kernel[1, 256]( - gains, bins, n, result_idx, result_gain, result_bin - ) - - return result_idx, result_gain, result_bin - - -# ============================================================================= -# Split Mask & Partitioning Kernels (Phase 2) -# ============================================================================= - -@cuda.jit -def _compute_split_mask_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - sample_indices: DeviceNDArray, # (n_subset,) int32 - feature: int32, - threshold: int32, - mask_out: DeviceNDArray, # (n_subset,) uint8 - 1=left, 0=right -): - """Compute boolean mask for split on GPU. - - mask[i] = 1 if binned[feature, sample_indices[i]] <= threshold, else 0 - """ - idx = cuda.grid(1) - n_subset = sample_indices.shape[0] - - if idx >= n_subset: - return - - sample_idx = sample_indices[idx] - bin_value = binned[feature, sample_idx] - mask_out[idx] = uint8(1) if bin_value <= threshold else uint8(0) - - -@cuda.jit -def _prefix_sum_kernel( - arr: DeviceNDArray, # (n,) int32 input - n: int32, - block_sums: DeviceNDArray, # (n_blocks,) int32 - sum per block - output: DeviceNDArray, # (n,) int32 - exclusive prefix sum -): - """Compute exclusive prefix sum within blocks. - - Uses shared memory for block-level scan. - Block sums are stored for later inter-block adjustment. - """ - thread_idx = cuda.threadIdx.x - block_idx = cuda.blockIdx.x - block_size = cuda.blockDim.x - global_idx = block_idx * block_size + thread_idx - - # Shared memory for block-level scan - shared = cuda.shared.array(512, dtype=int32) # 2x block_size for double-buffering - - # Load into shared memory - if global_idx < n: - shared[thread_idx] = arr[global_idx] - else: - shared[thread_idx] = 0 - cuda.syncthreads() - - # Up-sweep (reduce) phase - offset = 1 - d = block_size // 2 - while d > 0: - cuda.syncthreads() - if thread_idx < d: - ai = offset * (2 * thread_idx + 1) - 1 - bi = offset * (2 * thread_idx + 2) - 1 - if bi < block_size: - shared[bi] += shared[ai] - offset *= 2 - d //= 2 - - # Store block sum and clear last element for exclusive scan - if thread_idx == 0: - block_sums[block_idx] = shared[block_size - 1] - shared[block_size - 1] = 0 - cuda.syncthreads() - - # Down-sweep phase - d = 1 - while d < block_size: - offset //= 2 - cuda.syncthreads() - if thread_idx < d: - ai = offset * (2 * thread_idx + 1) - 1 - bi = offset * (2 * thread_idx + 2) - 1 - if bi < block_size: - t = shared[ai] - shared[ai] = shared[bi] - shared[bi] += t - d *= 2 - cuda.syncthreads() - - # Write result - if global_idx < n: - output[global_idx] = shared[thread_idx] - - -@cuda.jit -def _add_block_sums_kernel( - arr: DeviceNDArray, # (n,) int32 - prefix sums to adjust - block_sums: DeviceNDArray, # (n_blocks,) int32 - cumulative block sums - n: int32, -): - """Add block sums to complete global prefix sum.""" - block_idx = cuda.blockIdx.x - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - global_idx = block_idx * block_size + thread_idx - - if block_idx > 0 and global_idx < n: - arr[global_idx] += block_sums[block_idx - 1] - - -@cuda.jit -def _partition_by_mask_kernel( - sample_indices: DeviceNDArray, # (n_subset,) int32 - input indices - mask: DeviceNDArray, # (n_subset,) uint8 - 1=left, 0=right - prefix_sum: DeviceNDArray, # (n_subset,) int32 - exclusive prefix of mask - total_left: int32, # Total number of left samples - n_subset: int32, - left_out: DeviceNDArray, # (total_left,) int32 - left indices - right_out: DeviceNDArray, # (n_subset - total_left,) int32 - right indices -): - """Partition indices by mask using prefix sum scatter. - - Left indices go to positions [0, total_left) - Right indices go to positions [0, n_subset - total_left) - """ - idx = cuda.grid(1) - - if idx >= n_subset: - return - - sample_idx = sample_indices[idx] - - if mask[idx] == 1: - # Left: position is prefix_sum[idx] - left_out[prefix_sum[idx]] = sample_idx - else: - # Right: position is idx - prefix_sum[idx] (within right array) - right_pos = idx - prefix_sum[idx] - right_out[right_pos] = sample_idx - - -# ============================================================================= -# Simplified Partition Kernels (Phase 2 Fix) -# ============================================================================= - -@cuda.jit -def _copy_mask_to_int_kernel( - mask_in: DeviceNDArray, # (n,) uint8 - mask_out: DeviceNDArray, # (n,) int32 - n: int32, -): - """Copy uint8 mask to int32 for reduction. Module-level to avoid recompilation.""" - idx = cuda.grid(1) - if idx < n: - mask_out[idx] = int32(mask_in[idx]) - - -@cuda.jit -def _count_mask_kernel( - mask: DeviceNDArray, # (n,) uint8 - n: int32, - partial_counts: DeviceNDArray, # (n_blocks,) int32 -): - """Count ones in mask using parallel reduction.""" - thread_idx = cuda.threadIdx.x - block_idx = cuda.blockIdx.x - block_size = cuda.blockDim.x - - shared = cuda.shared.array(256, dtype=int32) - - # Each thread counts multiple elements - local_count = int32(0) - global_idx = block_idx * block_size + thread_idx - stride = block_size * cuda.gridDim.x - - while global_idx < n: - local_count += int32(mask[global_idx]) - global_idx += stride - - shared[thread_idx] = local_count - cuda.syncthreads() - - # Tree reduction - s = block_size // 2 - while s > 0: - if thread_idx < s: - shared[thread_idx] += shared[thread_idx + s] - cuda.syncthreads() - s //= 2 - - if thread_idx == 0: - partial_counts[block_idx] = shared[0] - - -@cuda.jit -def _scatter_by_mask_kernel( - sample_indices: DeviceNDArray, # (n,) int32 - input - mask: DeviceNDArray, # (n,) uint8 - 1=left, 0=right - left_counter: DeviceNDArray, # (1,) int32 - atomic counter for left - right_counter: DeviceNDArray, # (1,) int32 - atomic counter for right - n: int32, - left_out: DeviceNDArray, # (n_left,) int32 - right_out: DeviceNDArray, # (n_right,) int32 -): - """Scatter indices by mask using atomic counters. - - Simple two-pass approach: atomics for position, then write. - - NOTE: Known non-determinism -- The order of elements within left_out and - right_out is non-deterministic because cuda.atomic.add on the counters - does not guarantee a consistent ordering across threads/warps/blocks. - This means the output partitions will contain the same set of indices - but potentially in a different order on each run. - """ - idx = cuda.grid(1) - if idx >= n: - return - - sample_idx = sample_indices[idx] - - if mask[idx] == 1: - pos = cuda.atomic.add(left_counter, 0, 1) - left_out[pos] = sample_idx - else: - pos = cuda.atomic.add(right_counter, 0, 1) - right_out[pos] = sample_idx - - -def count_mask_cuda(mask: DeviceNDArray) -> int: - """Count ones in a mask array on GPU. - - Args: - mask: Boolean mask, shape (n,), uint8 - - Returns: - Number of ones (int) - """ - n = mask.shape[0] - - threads = 256 - n_blocks = min(256, math.ceil(n / threads)) - - partial_counts = cuda.device_array(n_blocks, dtype=np.int32) - - _count_mask_kernel[n_blocks, threads](mask, n, partial_counts) - - # Sum partial counts on CPU (small array) - counts_cpu = partial_counts.copy_to_host() - return int(np.sum(counts_cpu)) - - -def compute_split_mask_cuda( - binned: DeviceNDArray, - sample_indices: DeviceNDArray, - feature: int, - threshold: int, -) -> DeviceNDArray: - """Compute split mask on GPU. - - Args: - binned: Feature matrix, shape (n_features, n_samples), uint8 - sample_indices: Subset indices, shape (n_subset,), int32 - feature: Feature index to split on - threshold: Bin threshold (go left if <= threshold) - - Returns: - mask: Shape (n_subset,), uint8 - 1=left, 0=right - """ - n_subset = sample_indices.shape[0] - mask = cuda.device_array(n_subset, dtype=np.uint8) - - threads = 256 - blocks = math.ceil(n_subset / threads) - - _compute_split_mask_kernel[blocks, threads]( - binned, sample_indices, feature, threshold, mask - ) - - return mask - - -def partition_indices_cuda( - sample_indices: DeviceNDArray, - mask: DeviceNDArray, -) -> tuple[DeviceNDArray, DeviceNDArray, int, int]: - """Partition indices by mask - simplified count-then-scatter approach. - - Phase 2 Fix: Uses simple counting + atomic scatter instead of complex prefix sum. - This eliminates multiple copy_to_host() calls and JIT recompilation overhead. - - Args: - sample_indices: Indices to partition, shape (n,), int32 - mask: Boolean mask, shape (n,), uint8 - 1=left, 0=right - - Returns: - left_indices: Left partition, shape (n_left,), int32 - right_indices: Right partition, shape (n_right,), int32 - n_left: Number of left samples - n_right: Number of right samples - """ - n = sample_indices.shape[0] - - # Step 1: Count left samples (single reduction + one copy_to_host) - n_left = count_mask_cuda(mask) - n_right = n - n_left - - # Step 2: Allocate output arrays - left_indices = cuda.device_array(max(1, n_left), dtype=np.int32) - right_indices = cuda.device_array(max(1, n_right), dtype=np.int32) - - # Step 3: Scatter using atomic counters - if n_left > 0 and n_right > 0: - # Initialize atomic counters - left_counter = cuda.to_device(np.array([0], dtype=np.int32)) - right_counter = cuda.to_device(np.array([0], dtype=np.int32)) - - threads = 256 - blocks = math.ceil(n / threads) - - _scatter_by_mask_kernel[blocks, threads]( - sample_indices, mask, left_counter, right_counter, n, - left_indices, right_indices - ) - elif n_left > 0: - # All go left - direct copy on GPU - _copy_array_kernel[math.ceil(n / 256), 256](sample_indices, left_indices, n) - elif n_right > 0: - # All go right - direct copy on GPU - _copy_array_kernel[math.ceil(n / 256), 256](sample_indices, right_indices, n) - - return left_indices, right_indices, n_left, n_right - - -@cuda.jit -def _copy_array_kernel(src: DeviceNDArray, dst: DeviceNDArray, n: int32): - """Simple array copy kernel.""" - idx = cuda.grid(1) - if idx < n: - dst[idx] = src[idx] - - -def partition_samples_cuda( - binned: DeviceNDArray, - sample_indices: DeviceNDArray, - feature: int, - threshold: int, -) -> tuple[DeviceNDArray, DeviceNDArray, int, int]: - """Compute split and partition indices in one call. - - Convenience function combining mask computation and partitioning. - - Args: - binned: Feature matrix, shape (n_features, n_samples), uint8 - sample_indices: Indices to partition, shape (n,), int32 - feature: Feature index - threshold: Bin threshold - - Returns: - left_indices, right_indices, n_left, n_right - """ - mask = compute_split_mask_cuda(binned, sample_indices, feature, threshold) - return partition_indices_cuda(sample_indices, mask) - - -# ============================================================================= -# Gather Kernels (Phase 2) -# ============================================================================= - -@cuda.jit -def _gather_1d_kernel( - arr: DeviceNDArray, # (n_total,) - source array - indices: DeviceNDArray, # (n_subset,) int32 - indices to gather - n_subset: int32, - output: DeviceNDArray, # (n_subset,) - gathered values -): - """Gather elements by index for 1D arrays.""" - idx = cuda.grid(1) - if idx >= n_subset: - return - output[idx] = arr[indices[idx]] - - -@cuda.jit -def _gather_2d_kernel( - arr: DeviceNDArray, # (n_rows, n_cols) - source array - indices: DeviceNDArray, # (n_subset,) int32 - column indices to gather - n_rows: int32, - n_subset: int32, - output: DeviceNDArray, # (n_rows, n_subset) - gathered values -): - """Gather columns by index for 2D arrays (feature-major layout). - - Thread layout: 2D grid - - blockIdx.x, threadIdx.x: column (subset) dimension - - blockIdx.y, threadIdx.y: row (feature) dimension - """ - col_idx = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x - row_idx = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.y - - if col_idx >= n_subset or row_idx >= n_rows: - return - - src_col = indices[col_idx] - output[row_idx, col_idx] = arr[row_idx, src_col] - - -def gather_1d_cuda( - arr: DeviceNDArray, - indices: DeviceNDArray, -) -> DeviceNDArray: - """Gather elements by index on GPU for 1D arrays. - - Args: - arr: Source array, any dtype - indices: Indices to gather, shape (n_subset,), int32 - - Returns: - output: Shape (n_subset,) with same dtype as arr - """ - n_subset = indices.shape[0] - output = cuda.device_array(n_subset, dtype=arr.dtype) - - threads = 256 - blocks = math.ceil(n_subset / threads) - - _gather_1d_kernel[blocks, threads](arr, indices, n_subset, output) - - return output - - -def gather_2d_cuda( - arr: DeviceNDArray, - indices: DeviceNDArray, -) -> DeviceNDArray: - """Gather columns by index on GPU for 2D arrays. - - Args: - arr: Source array, shape (n_rows, n_cols), any dtype - indices: Column indices to gather, shape (n_subset,), int32 - - Returns: - output: Shape (n_rows, n_subset) with same dtype as arr - """ - n_rows = arr.shape[0] - n_subset = indices.shape[0] - output = cuda.device_array((n_rows, n_subset), dtype=arr.dtype) - - # 2D thread block layout - threads_x = 32 - threads_y = 8 - blocks_x = math.ceil(n_subset / threads_x) - blocks_y = math.ceil(n_rows / threads_y) - - _gather_2d_kernel[(blocks_x, blocks_y), (threads_x, threads_y)]( - arr, indices, n_rows, n_subset, output - ) - - return output - - -def gather_cuda(arr: DeviceNDArray, indices: DeviceNDArray) -> DeviceNDArray: - """Gather elements by index on GPU (auto-dispatch 1D/2D). - - For 1D arrays: output = arr[indices] - For 2D arrays: output = arr[:, indices] (column gather) - - Args: - arr: Source array - indices: Indices to gather - - Returns: - Gathered array on GPU - """ - if arr.ndim == 1: - return gather_1d_cuda(arr, indices) - elif arr.ndim == 2: - return gather_2d_cuda(arr, indices) - else: - raise ValueError(f"gather_cuda only supports 1D and 2D arrays, got {arr.ndim}D") - - -# ============================================================================= -# Split Finding Kernel (Phase 3.3: float32) -# ============================================================================= - -@cuda.jit -def _find_best_split_kernel( - hist_grad: DeviceNDArray, # (n_features, 256) float32 - hist_hess: DeviceNDArray, # (n_features, 256) float32 - total_grad: float32, - total_hess: float32, - reg_lambda: float32, - min_child_weight: float32, - best_gains: DeviceNDArray, # (n_features,) float64 - best gain per feature - best_bins: DeviceNDArray, # (n_features,) int32 - best bin per feature -): - """Find the best split for each feature. - - Each thread handles one feature, scanning all bins. - Gain computation uses float64 for parity with CPU split-finding. - """ - feature_idx = cuda.grid(1) - n_features = hist_grad.shape[0] - - if feature_idx >= n_features: - return - - # Cast totals to float64 for gain computation (parity with CPU) - total_grad_64 = float64(total_grad) - total_hess_64 = float64(total_hess) - reg_lambda_64 = float64(reg_lambda) - min_cw_64 = float64(min_child_weight) - - # Parent gain (constant for this split) - parent_gain = (total_grad_64 * total_grad_64) / (total_hess_64 + reg_lambda_64) - - best_gain = float64(-1e10) - best_bin = int32(-1) - - # Cumulative sums for left child (float64 for precision) - left_grad = float64(0.0) - left_hess = float64(0.0) - - # Scan through bins (split point is "go left if bin <= threshold") - for bin_idx in range(255): # Can't split on last bin - left_grad += float64(hist_grad[feature_idx, bin_idx]) - left_hess += float64(hist_hess[feature_idx, bin_idx]) - - right_grad = total_grad_64 - left_grad - right_hess = total_hess_64 - left_hess - - # Check min_child_weight constraint - if left_hess < min_cw_64 or right_hess < min_cw_64: - continue - - # Compute gain in float64 - left_score = (left_grad * left_grad) / (left_hess + reg_lambda_64) - right_score = (right_grad * right_grad) / (right_hess + reg_lambda_64) - gain = left_score + right_score - parent_gain - - if gain > best_gain: - best_gain = gain - best_bin = bin_idx - - best_gains[feature_idx] = float64(best_gain) - best_bins[feature_idx] = best_bin - - -def find_best_split_cuda( - hist_grad: DeviceNDArray, - hist_hess: DeviceNDArray, - total_grad: float, - total_hess: float, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, -) -> tuple[int, int, float]: - """Find the best split across all features. - - Args: - hist_grad: Gradient histogram, shape (n_features, 256) - hist_hess: Hessian histogram, shape (n_features, 256) - total_grad: Sum of all gradients - total_hess: Sum of all hessians - reg_lambda: L2 regularization - min_child_weight: Minimum sum of hessian in child - - Returns: - best_feature: Index of best feature to split on (-1 if no valid split) - best_bin: Bin threshold for split - best_gain: Gain from the split - """ - n_features = hist_grad.shape[0] - - # Guard for zero features - if n_features == 0: - return -1, -1, 0.0 - - # Allocate output arrays (float64 for gain precision parity with CPU) - best_gains = cuda.device_array(n_features, dtype=np.float64) - best_bins = cuda.device_array(n_features, dtype=np.int32) - - # Launch kernel - threads = 256 - blocks = math.ceil(n_features / threads) - - _find_best_split_kernel[blocks, threads]( - hist_grad, hist_hess, - np.float32(total_grad), np.float32(total_hess), - np.float32(reg_lambda), np.float32(min_child_weight), - best_gains, best_bins, - ) - - # Find global best (small array, OK to copy to CPU) - gains_cpu = best_gains.copy_to_host() - bins_cpu = best_bins.copy_to_host() - - best_feature = int(np.argmax(gains_cpu)) - best_gain = float(gains_cpu[best_feature]) - best_bin = int(bins_cpu[best_feature]) - - if best_gain <= 0 or best_bin < 0: - return -1, -1, 0.0 - - return best_feature, best_bin, best_gain - - -# ============================================================================= -# Prediction Kernel -# ============================================================================= - -@cuda.jit -def _predict_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - tree_features: DeviceNDArray, # (n_nodes,) int32 - feature index per node - tree_thresholds: DeviceNDArray, # (n_nodes,) uint8 - bin threshold - tree_values: DeviceNDArray, # (n_nodes,) float32 - leaf values - tree_left: DeviceNDArray, # (n_nodes,) int32 - left child index - tree_right: DeviceNDArray, # (n_nodes,) int32 - right child index - predictions: DeviceNDArray, # (n_samples,) float32 - output -): - """Traverse tree for each sample. - - Node convention: - - tree_left[i] == -1 means node i is a leaf - - For internal nodes: go left if binned[feature, sample] <= threshold - """ - sample_idx = cuda.grid(1) - n_samples = binned.shape[1] - - if sample_idx >= n_samples: - return - - # Start at root - node = 0 - - # Traverse until leaf - while tree_left[node] != -1: - feature = tree_features[node] - threshold = tree_thresholds[node] - bin_value = binned[feature, sample_idx] - - node = tree_left[node] if bin_value <= threshold else tree_right[node] - - predictions[sample_idx] = tree_values[node] - - -def predict_cuda( - binned: DeviceNDArray, - tree_features: DeviceNDArray, - tree_thresholds: DeviceNDArray, - tree_values: DeviceNDArray, - tree_left: DeviceNDArray, - tree_right: DeviceNDArray, - tree_missing_left: DeviceNDArray | None = None, -) -> DeviceNDArray: - """Predict using a tree on GPU. - - Phase 14.2: Added support for missing value handling. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples) - tree_*: Tree structure arrays - tree_missing_left: Direction for missing values, shape (n_nodes,), bool - - Returns: - predictions: Shape (n_samples,), float32 - """ - n_samples = binned.shape[1] - predictions = cuda.device_array(n_samples, dtype=np.float32) - - threads = 256 - blocks = math.ceil(n_samples / threads) - - if tree_missing_left is not None: - _predict_with_missing_kernel[blocks, threads]( - binned, tree_features, tree_thresholds, tree_values, - tree_left, tree_right, tree_missing_left, predictions - ) - else: - _predict_kernel[blocks, threads]( - binned, tree_features, tree_thresholds, tree_values, - tree_left, tree_right, predictions - ) - - return predictions - - -# ============================================================================= -# Phase 14.2: Missing Value Handling Kernels -# ============================================================================= - -MISSING_BIN_GPU = 255 # Reserved bin for missing values - - -@cuda.jit -def _predict_with_missing_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - tree_features: DeviceNDArray, # (n_nodes,) int32 - tree_thresholds: DeviceNDArray, # (n_nodes,) uint8 - tree_values: DeviceNDArray, # (n_nodes,) float32 - tree_left: DeviceNDArray, # (n_nodes,) int32 - tree_right: DeviceNDArray, # (n_nodes,) int32 - tree_missing_left: DeviceNDArray, # (n_nodes,) bool - Phase 14.2 - predictions: DeviceNDArray, # (n_samples,) float32 -): - """Traverse tree with missing value handling. - - Phase 14.2: Missing values (bin 255) are routed according to - the learned direction stored in tree_missing_left. - """ - sample_idx = cuda.grid(1) - n_samples = binned.shape[1] - - if sample_idx >= n_samples: - return - - node = 0 - - while tree_left[node] != -1: - feature = tree_features[node] - threshold = tree_thresholds[node] - bin_value = binned[feature, sample_idx] - - # Phase 14.2: Check for missing value - if bin_value == 255: # MISSING_BIN - node = tree_left[node] if tree_missing_left[node] else tree_right[node] - elif bin_value <= threshold: - node = tree_left[node] - else: - node = tree_right[node] - - predictions[sample_idx] = tree_values[node] - - -@cuda.jit -def _predict_with_categorical_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - tree_features: DeviceNDArray, # (n_nodes,) int32 - tree_thresholds: DeviceNDArray, # (n_nodes,) uint8 - tree_values: DeviceNDArray, # (n_nodes,) float32 - tree_left: DeviceNDArray, # (n_nodes,) int32 - tree_right: DeviceNDArray, # (n_nodes,) int32 - tree_missing_left: DeviceNDArray, # (n_nodes,) bool - is_categorical_split: DeviceNDArray, # (n_nodes,) bool - Phase 14.4 - cat_bitsets: DeviceNDArray, # (n_nodes,) int64 - Phase 14.4 - predictions: DeviceNDArray, # (n_samples,) float32 -): - """Traverse tree with categorical and missing value handling. - - Phase 14.4: Supports categorical splits using bitmask routing. - For categorical splits, go left if (1 << bin_value) & cat_bitset != 0. - """ - sample_idx = cuda.grid(1) - n_samples = binned.shape[1] - - if sample_idx >= n_samples: - return - - node = 0 - - while tree_left[node] != -1: - feature = tree_features[node] - bin_value = binned[feature, sample_idx] - - # Check for missing value first - if bin_value == 255: # MISSING_BIN - node = tree_left[node] if tree_missing_left[node] else tree_right[node] - elif is_categorical_split[node]: - # Categorical split: use bitmask. Bins >= 64 are not representable - # in the 64-bit bitset and always go right. - bitset = cat_bitsets[node] - goes_left = False - if bin_value < 64: - goes_left = ((int64(1) << bin_value) & bitset) != 0 - node = tree_left[node] if goes_left else tree_right[node] - else: - # Numeric split: use threshold - threshold = tree_thresholds[node] - node = tree_left[node] if bin_value <= threshold else tree_right[node] - - predictions[sample_idx] = tree_values[node] - - -def predict_with_categorical_cuda( - binned: DeviceNDArray, - tree_features: DeviceNDArray, - tree_thresholds: DeviceNDArray, - tree_values: DeviceNDArray, - tree_left: DeviceNDArray, - tree_right: DeviceNDArray, - tree_missing_left: DeviceNDArray | None = None, - is_categorical_split: DeviceNDArray | None = None, - cat_bitsets: DeviceNDArray | None = None, -) -> DeviceNDArray: - """Predict using a tree with categorical support on GPU. - - Phase 14.4: GPU prediction supporting categorical splits. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples) - tree_*: Tree structure arrays - tree_missing_left: Direction for missing values - is_categorical_split: Whether each node is categorical - cat_bitsets: Bitmasks for categorical splits - - Returns: - predictions: Shape (n_samples,), float32 - """ - n_samples = binned.shape[1] - n_nodes = tree_features.shape[0] - predictions = cuda.device_array(n_samples, dtype=np.float32) - - threads = 256 - blocks = math.ceil(n_samples / threads) - - # Prepare default arrays if not provided - if tree_missing_left is None: - tree_missing_left = cuda.to_device(np.ones(n_nodes, dtype=np.bool_)) - if is_categorical_split is None: - is_categorical_split = cuda.to_device(np.zeros(n_nodes, dtype=np.bool_)) - if cat_bitsets is None: - cat_bitsets = cuda.to_device(np.zeros(n_nodes, dtype=np.int64)) - - _predict_with_categorical_kernel[blocks, threads]( - binned, tree_features, tree_thresholds, tree_values, - tree_left, tree_right, tree_missing_left, - is_categorical_split, cat_bitsets, predictions - ) - - return predictions - - -@cuda.jit -def _find_best_split_with_missing_kernel( - hist_grad: DeviceNDArray, # (n_features, 256) float32 - hist_hess: DeviceNDArray, # (n_features, 256) float32 - has_missing: DeviceNDArray, # (n_features,) bool - total_grad: float32, - total_hess: float32, - reg_lambda: float32, - min_child_weight: float32, - best_gains: DeviceNDArray, # (n_features,) float32 - best_bins: DeviceNDArray, # (n_features,) int32 - best_missing_left: DeviceNDArray, # (n_features,) bool - Phase 14.2 -): - """Find the best split considering missing values. - - Phase 14.2: For each split candidate, tries both directions for missing: - 1. Missing goes LEFT - 2. Missing goes RIGHT - - Picks whichever gives higher gain. - """ - feature_idx = cuda.grid(1) - n_features = hist_grad.shape[0] - - if feature_idx >= n_features: - return - - # Get missing statistics for this feature - miss_grad = hist_grad[feature_idx, 255] # MISSING_BIN - miss_hess = hist_hess[feature_idx, 255] - feature_has_missing = has_missing[feature_idx] - - # Total for non-missing values - if feature_has_missing: - nonmiss_total_grad = total_grad - miss_grad - nonmiss_total_hess = total_hess - miss_hess - else: - nonmiss_total_grad = total_grad - nonmiss_total_hess = total_hess - - # Parent gain - parent_gain = (total_grad * total_grad) / (total_hess + reg_lambda) - - best_gain = float32(-1e10) - best_bin = int32(-1) - best_miss_left = True - - # Cumulative sums for left child - left_grad = float32(0.0) - left_hess = float32(0.0) - - # Scan through bins 0-254 (not 255 which is missing) - for bin_idx in range(255): - left_grad += hist_grad[feature_idx, bin_idx] - left_hess += hist_hess[feature_idx, bin_idx] - - right_grad = nonmiss_total_grad - left_grad - right_hess = nonmiss_total_hess - left_hess - - if feature_has_missing and (miss_grad != float32(0.0) or miss_hess != float32(0.0)): - # Try missing goes LEFT - left_g_miss = left_grad + miss_grad - left_h_miss = left_hess + miss_hess - - if left_h_miss >= min_child_weight and right_hess >= min_child_weight: - left_score = (left_g_miss * left_g_miss) / (left_h_miss + reg_lambda) - right_score = (right_grad * right_grad) / (right_hess + reg_lambda) - gain_miss_left = left_score + right_score - parent_gain - - if gain_miss_left > best_gain: - best_gain = gain_miss_left - best_bin = bin_idx - best_miss_left = True - - # Try missing goes RIGHT - right_g_miss = right_grad + miss_grad - right_h_miss = right_hess + miss_hess - - if left_hess >= min_child_weight and right_h_miss >= min_child_weight: - left_score = (left_grad * left_grad) / (left_hess + reg_lambda) - right_score = (right_g_miss * right_g_miss) / (right_h_miss + reg_lambda) - gain_miss_right = left_score + right_score - parent_gain - - if gain_miss_right > best_gain: - best_gain = gain_miss_right - best_bin = bin_idx - best_miss_left = False - else: - # No missing values - standard split - if left_hess >= min_child_weight and right_hess >= min_child_weight: - left_score = (left_grad * left_grad) / (left_hess + reg_lambda) - right_score = (right_grad * right_grad) / (right_hess + reg_lambda) - gain = left_score + right_score - parent_gain - - if gain > best_gain: - best_gain = gain - best_bin = bin_idx - best_miss_left = True # Default direction - - best_gains[feature_idx] = best_gain - best_bins[feature_idx] = best_bin - best_missing_left[feature_idx] = best_miss_left - - -def find_best_split_with_missing_cuda( - hist_grad: DeviceNDArray, - hist_hess: DeviceNDArray, - total_grad: float, - total_hess: float, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - has_missing: np.ndarray | None = None, -) -> tuple[int, int, float, bool]: - """Find the best split considering missing values on GPU. - - Phase 14.2: GPU implementation of missing-aware split finding. - - Args: - hist_grad: Gradient histogram, shape (n_features, 256) - hist_hess: Hessian histogram, shape (n_features, 256) - total_grad: Sum of all gradients - total_hess: Sum of all hessians - reg_lambda: L2 regularization - min_child_weight: Minimum sum of hessian in child - has_missing: Boolean array (n_features,) indicating which features have NaN - - Returns: - best_feature: Index of best feature (-1 if no valid split) - best_bin: Bin threshold for split - best_gain: Gain from the split - missing_go_left: Whether missing values should go left - """ - n_features = hist_grad.shape[0] - - # Allocate output arrays - best_gains = cuda.device_array(n_features, dtype=np.float32) - best_bins = cuda.device_array(n_features, dtype=np.int32) - best_missing_left = cuda.device_array(n_features, dtype=np.bool_) - - # Prepare has_missing on GPU - if has_missing is None: - has_missing = np.zeros(n_features, dtype=np.bool_) - has_missing_gpu = cuda.to_device(has_missing.astype(np.bool_)) - - # Launch kernel - threads = 256 - blocks = math.ceil(n_features / threads) - - _find_best_split_with_missing_kernel[blocks, threads]( - hist_grad, hist_hess, has_missing_gpu, - np.float32(total_grad), np.float32(total_hess), - np.float32(reg_lambda), np.float32(min_child_weight), - best_gains, best_bins, best_missing_left, - ) - - # Find global best (small array, OK to copy to CPU) - gains_cpu = best_gains.copy_to_host() - bins_cpu = best_bins.copy_to_host() - missing_left_cpu = best_missing_left.copy_to_host() - - best_feature = int(np.argmax(gains_cpu)) - best_gain = float(gains_cpu[best_feature]) - best_bin = int(bins_cpu[best_feature]) - best_miss_left = bool(missing_left_cpu[best_feature]) - - if best_gain <= 0 or best_bin < 0: - return -1, -1, 0.0, True - - return best_feature, best_bin, best_gain, best_miss_left - - -# ============================================================================= -# Phase 14.4: Categorical Split Finding GPU Kernels -# ============================================================================= - -@cuda.jit(device=True) -def _compute_gain_device(left_g: float32, left_h: float32, - right_g: float32, right_h: float32, - reg_lambda: float32) -> float32: - """Device function to compute split gain.""" - left_score = (left_g * left_g) / (left_h + reg_lambda) - right_score = (right_g * right_g) / (right_h + reg_lambda) - return left_score + right_score - - -@cuda.jit -def _find_best_categorical_split_kernel( - hist_grad: DeviceNDArray, # (n_features, 256) float32 - hist_hess: DeviceNDArray, # (n_features, 256) float32 - is_categorical: DeviceNDArray, # (n_features,) bool - n_categories: DeviceNDArray, # (n_features,) int32 - has_missing: DeviceNDArray, # (n_features,) bool - total_grad: float32, - total_hess: float32, - reg_lambda: float32, - min_child_weight: float32, - # Outputs: - best_gains: DeviceNDArray, # (n_features,) float32 - best_thresholds: DeviceNDArray, # (n_features,) int32 - best_missing_left: DeviceNDArray, # (n_features,) bool - best_is_cat: DeviceNDArray, # (n_features,) bool - best_cat_bitsets: DeviceNDArray, # (n_features,) int64 -): - """Find best split per feature - handles both categorical and numeric. - - Phase 14.4: For categorical features, uses Fisher's optimal ordering. - Each thread handles one feature. - - For categorical: Sort categories by G/(H+λ), find best split in sorted order. - For numeric: Standard ordinal split with missing value handling. - """ - feature_idx = cuda.grid(1) - n_features = hist_grad.shape[0] - - if feature_idx >= n_features: - return - - # Get missing stats - miss_grad = hist_grad[feature_idx, 255] - miss_hess = hist_hess[feature_idx, 255] - has_miss = has_missing[feature_idx] - - # Parent gain - parent_gain = (total_grad * total_grad) / (total_hess + reg_lambda) - - if is_categorical[feature_idx]: - # === CATEGORICAL SPLIT === - n_cats = n_categories[feature_idx] - - # Local arrays for sorting (max 254 categories) - # Using thread-local registers/arrays - cat_scores = cuda.local.array(256, dtype=float32) - cat_grads = cuda.local.array(256, dtype=float32) - cat_hess_arr = cuda.local.array(256, dtype=float32) - sorted_cats = cuda.local.array(256, dtype=int32) - - # Compute scores and initialize sorted order - total_cat_g = float32(0.0) - total_cat_h = float32(0.0) - - for cat in range(n_cats): - g = hist_grad[feature_idx, cat] - h = hist_hess[feature_idx, cat] - cat_grads[cat] = g - cat_hess_arr[cat] = h - total_cat_g += g - total_cat_h += h - - # Fisher score: -G / (H + lambda) - if h > float32(1e-10): - cat_scores[cat] = -g / (h + reg_lambda) - else: - cat_scores[cat] = float32(0.0) - sorted_cats[cat] = cat - - # Simple selection sort (O(n^2) but n <= 254, runs in registers) - for i in range(n_cats - 1): - min_idx = i - min_val = cat_scores[sorted_cats[i]] - for j in range(i + 1, n_cats): - if cat_scores[sorted_cats[j]] < min_val: - min_val = cat_scores[sorted_cats[j]] - min_idx = j - # Swap - if min_idx != i: - tmp = sorted_cats[i] - sorted_cats[i] = sorted_cats[min_idx] - sorted_cats[min_idx] = tmp - - # Find best split in sorted order - best_gain_cat = float32(-1e10) - best_split = int32(0) - best_miss_left_cat = True - - left_g = float32(0.0) - left_h = float32(0.0) - - for i in range(n_cats - 1): - cat = sorted_cats[i] - left_g += cat_grads[cat] - left_h += cat_hess_arr[cat] - - right_g = total_cat_g - left_g - right_h = total_cat_h - left_h - - if has_miss and (miss_grad != float32(0.0) or miss_hess != float32(0.0)): - # Try missing LEFT - if left_h + miss_hess >= min_child_weight and right_h >= min_child_weight: - gain = _compute_gain_device(left_g + miss_grad, left_h + miss_hess, - right_g, right_h, reg_lambda) - parent_gain - if gain > best_gain_cat: - best_gain_cat = gain - best_split = i + 1 - best_miss_left_cat = True - - # Try missing RIGHT - if left_h >= min_child_weight and right_h + miss_hess >= min_child_weight: - gain = _compute_gain_device(left_g, left_h, - right_g + miss_grad, right_h + miss_hess, - reg_lambda) - parent_gain - if gain > best_gain_cat: - best_gain_cat = gain - best_split = i + 1 - best_miss_left_cat = False - else: - if left_h >= min_child_weight and right_h >= min_child_weight: - gain = _compute_gain_device(left_g, left_h, right_g, right_h, - reg_lambda) - parent_gain - if gain > best_gain_cat: - best_gain_cat = gain - best_split = i + 1 - best_miss_left_cat = True - - # Build bitmask: categories before split_point go left. - # cat_bitset is 64-bit; category bins >= 64 cannot be represented and - # are always routed right (consistent with CPU split-finding/predict). - cat_bitset = int64(0) - for i in range(best_split): - cat = sorted_cats[i] - if cat < 64: - cat_bitset |= (int64(1) << cat) - - best_gains[feature_idx] = best_gain_cat - best_thresholds[feature_idx] = best_split - best_missing_left[feature_idx] = best_miss_left_cat - best_is_cat[feature_idx] = True - best_cat_bitsets[feature_idx] = cat_bitset - - else: - # === NUMERIC SPLIT === - nonmiss_total_grad = total_grad - miss_grad if has_miss else total_grad - nonmiss_total_hess = total_hess - miss_hess if has_miss else total_hess - - best_gain_num = float32(-1e10) - best_bin = int32(-1) - best_miss_left_num = True - - left_grad = float32(0.0) - left_hess = float32(0.0) - - for bin_idx in range(255): - left_grad += hist_grad[feature_idx, bin_idx] - left_hess += hist_hess[feature_idx, bin_idx] - - right_grad = nonmiss_total_grad - left_grad - right_hess = nonmiss_total_hess - left_hess - - if has_miss and (miss_grad != float32(0.0) or miss_hess != float32(0.0)): - # Try missing LEFT - if left_hess + miss_hess >= min_child_weight and right_hess >= min_child_weight: - gain = _compute_gain_device(left_grad + miss_grad, left_hess + miss_hess, - right_grad, right_hess, reg_lambda) - parent_gain - if gain > best_gain_num: - best_gain_num = gain - best_bin = bin_idx - best_miss_left_num = True - - # Try missing RIGHT - if left_hess >= min_child_weight and right_hess + miss_hess >= min_child_weight: - gain = _compute_gain_device(left_grad, left_hess, - right_grad + miss_grad, right_hess + miss_hess, - reg_lambda) - parent_gain - if gain > best_gain_num: - best_gain_num = gain - best_bin = bin_idx - best_miss_left_num = False - else: - if left_hess >= min_child_weight and right_hess >= min_child_weight: - gain = _compute_gain_device(left_grad, left_hess, right_grad, right_hess, - reg_lambda) - parent_gain - if gain > best_gain_num: - best_gain_num = gain - best_bin = bin_idx - best_miss_left_num = True - - best_gains[feature_idx] = best_gain_num - best_thresholds[feature_idx] = best_bin - best_missing_left[feature_idx] = best_miss_left_num - best_is_cat[feature_idx] = False - best_cat_bitsets[feature_idx] = int64(0) - - -def find_best_split_categorical_cuda( - hist_grad: DeviceNDArray, - hist_hess: DeviceNDArray, - total_grad: float, - total_hess: float, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - is_categorical: np.ndarray | None = None, - n_categories: np.ndarray | None = None, - has_missing: np.ndarray | None = None, -) -> tuple[int, int, float, bool, bool, int, int]: - """Find best split considering both categorical and numeric features on GPU. - - Phase 14.4: GPU implementation supporting mixed feature types. - - Args: - hist_grad: Gradient histogram, shape (n_features, 256) - hist_hess: Hessian histogram, shape (n_features, 256) - total_grad: Sum of all gradients - total_hess: Sum of all hessians - reg_lambda: L2 regularization - min_child_weight: Minimum sum of hessian in child - is_categorical: Boolean array indicating categorical features - n_categories: Number of categories per feature - has_missing: Boolean array indicating features with missing values - - Returns: - best_feature: Index of best feature (-1 if no valid split) - best_threshold: Bin threshold (ordinal) or split point (categorical) - best_gain: Gain from the split - missing_go_left: Direction for missing values - is_cat_split: Whether this is a categorical split - cat_bitset: Bitmask for categorical split (0 for numeric) - cat_threshold: Category threshold in sorted order - """ - n_features = hist_grad.shape[0] - - # Prepare arrays on GPU - if is_categorical is None: - is_categorical = np.zeros(n_features, dtype=np.bool_) - if n_categories is None: - n_categories = np.zeros(n_features, dtype=np.int32) - if has_missing is None: - has_missing = np.zeros(n_features, dtype=np.bool_) - - is_cat_gpu = cuda.to_device(is_categorical.astype(np.bool_)) - n_cats_gpu = cuda.to_device(n_categories.astype(np.int32)) - has_miss_gpu = cuda.to_device(has_missing.astype(np.bool_)) - - # Allocate outputs - best_gains = cuda.device_array(n_features, dtype=np.float32) - best_thresholds = cuda.device_array(n_features, dtype=np.int32) - best_missing_left = cuda.device_array(n_features, dtype=np.bool_) - best_is_cat = cuda.device_array(n_features, dtype=np.bool_) - best_cat_bitsets = cuda.device_array(n_features, dtype=np.int64) - - # Launch kernel - threads = 256 - blocks = math.ceil(n_features / threads) - - _find_best_categorical_split_kernel[blocks, threads]( - hist_grad, hist_hess, - is_cat_gpu, n_cats_gpu, has_miss_gpu, - np.float32(total_grad), np.float32(total_hess), - np.float32(reg_lambda), np.float32(min_child_weight), - best_gains, best_thresholds, best_missing_left, - best_is_cat, best_cat_bitsets, - ) - - # Find global best - gains_cpu = best_gains.copy_to_host() - thresholds_cpu = best_thresholds.copy_to_host() - missing_left_cpu = best_missing_left.copy_to_host() - is_cat_cpu = best_is_cat.copy_to_host() - cat_bitsets_cpu = best_cat_bitsets.copy_to_host() - - best_feature = int(np.argmax(gains_cpu)) - best_gain = float(gains_cpu[best_feature]) - best_threshold = int(thresholds_cpu[best_feature]) - best_miss_left = bool(missing_left_cpu[best_feature]) - is_cat_split = bool(is_cat_cpu[best_feature]) - cat_bitset = int(cat_bitsets_cpu[best_feature]) - - if best_gain <= 0 or best_threshold < 0: - return -1, -1, 0.0, True, False, 0, -1 - - cat_thresh = best_threshold if is_cat_split else -1 - return (best_feature, best_threshold, best_gain, best_miss_left, - is_cat_split, cat_bitset, cat_thresh) - - -# ============================================================================= -# Utility Functions -# ============================================================================= - -def to_device(arr: np.ndarray) -> DeviceNDArray: - """Transfer numpy array to GPU.""" - return cuda.to_device(arr) - - -def as_cuda_array(arr) -> DeviceNDArray: - """Wrap array with __cuda_array_interface__ as Numba device array.""" - if hasattr(arr, '__cuda_array_interface__'): - return cuda.as_cuda_array(arr) - raise TypeError(f"Cannot convert {type(arr)} to CUDA array") - - -def synchronize(): - """Synchronize CUDA device.""" - cuda.synchronize() - - -# ============================================================================= -# Phase 3.2: GPU-Native Tree Building (updated 3.3: float32) -# ============================================================================= - -@cuda.jit -def _init_sample_nodes_kernel(sample_node_ids, n_samples): - """Initialize all samples to root node (node 0).""" - idx = cuda.grid(1) - if idx < n_samples: - sample_node_ids[idx] = 0 - - -@cuda.jit -def _zero_float_array_kernel(arr, n): - """Zero out a float array (works with both float32 and float64).""" - idx = cuda.grid(1) - if idx < n: - arr[idx] = 0.0 - - -@cuda.jit -def _copy_to_slot_kernel(src, dst, slot_offset, n): - """Copy n elements from src to dst[slot_offset:slot_offset+n].""" - idx = cuda.grid(1) - if idx < n: - dst[slot_offset + idx] = src[idx] - - -@cuda.jit -def _init_tree_nodes_kernel(features, thresholds, left, right, max_n): - """Initialize all tree nodes as leaves. - - Phase 3.6: Moved to module level to avoid JIT dispatch overhead. - Previously defined inside build_tree_gpu_native, causing 31.9ms overhead per call. - """ - idx = cuda.grid(1) - if idx < max_n: - features[idx] = int32(-1) - thresholds[idx] = int32(-1) - left[idx] = int32(-1) - right[idx] = int32(-1) - - -@cuda.jit -def _build_root_histogram_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - histograms, # (max_nodes, n_features, 256, 2) float32 - output -): - """Phase 6.1: Optimized histogram for root node - NO sample_node_ids check. - - At depth 0, ALL samples belong to root (node 0). No need to check sample_node_ids. - This eliminates 1M branch checks per tree, giving ~2.5x speedup at depth 0. - - Grid: (n_features,) - Block: 256 threads - """ - feature_idx = cuda.blockIdx.x - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory for local histogram - local_grad = cuda.shared.array(256, dtype=float32) - local_hess = cuda.shared.array(256, dtype=float32) - - # Initialize shared memory - for i in range(thread_idx, 256, block_size): - local_grad[i] = float32(0.0) - local_hess[i] = float32(0.0) - cuda.syncthreads() - - # NO BRANCH - all samples contribute to root histogram - for sample_idx in range(thread_idx, n_samples, block_size): - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = hess[sample_idx] - cuda.atomic.add(local_grad, bin_val, g) - cuda.atomic.add(local_hess, bin_val, h) - cuda.syncthreads() - - # Write to root histogram (node 0) - for i in range(thread_idx, 256, block_size): - histograms[0, feature_idx, i, 0] = local_grad[i] - histograms[0, feature_idx, i, 1] = local_hess[i] - - -@cuda.jit -def _build_level_histograms_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - which node each sample belongs to - level_start, # int32 - first node index at this level - level_end, # int32 - last node index + 1 at this level - histograms, # (max_nodes, n_features, 256, 2) float32 - output -): - """Build histograms for all nodes at current level. - - Thread layout: - - blockIdx.x = feature index - - blockIdx.y = node offset within level (0 to level_end - level_start - 1) - - threads cooperate to process all samples - - Each block handles one (node, feature) pair. - Phase 3.3: All computations in float32. - """ - feature_idx = cuda.blockIdx.x - node_offset = cuda.blockIdx.y - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - node_idx = level_start + node_offset - if node_idx >= level_end: - return - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory for local histogram (Phase 3.3: float32) - local_grad = cuda.shared.array(256, dtype=float32) - local_hess = cuda.shared.array(256, dtype=float32) - - # Initialize shared memory - for i in range(thread_idx, 256, block_size): - local_grad[i] = float32(0.0) - local_hess[i] = float32(0.0) - cuda.syncthreads() - - # Each thread processes samples, accumulating those belonging to this node - for sample_idx in range(thread_idx, n_samples, block_size): - if sample_node_ids[sample_idx] == node_idx: - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = hess[sample_idx] - cuda.atomic.add(local_grad, bin_val, g) - cuda.atomic.add(local_hess, bin_val, h) - cuda.syncthreads() - - # Write to global memory - for i in range(thread_idx, 256, block_size): - histograms[node_idx, feature_idx, i, 0] = local_grad[i] - histograms[node_idx, feature_idx, i, 1] = local_hess[i] - - -@cuda.jit -def _zero_level_histograms_kernel( - histograms, # (max_nodes, n_features, 256, 2) float32 - level_start, # int32 - first node index at this level - level_end, # int32 - last node index + 1 at this level - n_features, # int32 -): - """Phase 6.2: Zero histogram entries for nodes at current level. - - Grid: (n_nodes_at_level, n_features) - Block: 256 threads (one per bin) - """ - node_offset = cuda.blockIdx.x - feature_idx = cuda.blockIdx.y - bin_idx = cuda.threadIdx.x - - node_idx = level_start + node_offset - if node_idx >= level_end or feature_idx >= n_features or bin_idx >= 256: - return - - histograms[node_idx, feature_idx, bin_idx, 0] = 0.0 - histograms[node_idx, feature_idx, bin_idx, 1] = 0.0 - - -@cuda.jit -def _build_histograms_sample_centric_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - which node each sample belongs to - level_start, # int32 - first node index at this level - level_end, # int32 - last node index + 1 at this level - histograms, # (max_nodes, n_features, 256, 2) float32 - output -): - """Phase 6.2: Sample-centric histogram building. - - Key insight: Instead of each block scanning ALL samples for ONE node, - each block processes a CHUNK of samples for ONE feature, updating - whichever nodes those samples belong to. - - This reduces work from O(nodes × samples) to O(samples) per level. - At depth 5: 5.3x less work (3.2B → 600M reads). - - Grid: (n_features, n_sample_chunks) # (100, 245) = 24,500 blocks - Block: 256 threads - Each block processes ~4K samples for ONE feature - """ - feature_idx = cuda.blockIdx.x - chunk_idx = cuda.blockIdx.y - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - CHUNK_SIZE = 32768 - chunk_start = chunk_idx * CHUNK_SIZE - chunk_end = chunk_start + CHUNK_SIZE - if chunk_end > n_samples: - chunk_end = n_samples - - # Process samples in this chunk - for sample_idx in range(chunk_start + thread_idx, chunk_end, block_size): - node_id = sample_node_ids[sample_idx] - - # Only process if sample belongs to a node at current level - if level_start <= node_id < level_end: - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = hess[sample_idx] - # Global atomics - OK because A100 has fast FP32 atomics - # and threads hit different (node, feature, bin) tuples - cuda.atomic.add(histograms, (node_id, feature_idx, bin_val, 0), g) - cuda.atomic.add(histograms, (node_id, feature_idx, bin_val, 1), h) - - -@cuda.jit -def _build_histogram_shared_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - level_start, # int32 - first node index at this level - n_nodes_in_pass, # int32 - how many nodes in this pass (≤16) - node_offset, # int32 - which nodes this pass handles (0 or 16) - global_histograms,# (max_nodes, n_features, 256, 2) float32 - output - const_hess, # float32 - >0: use this instead of reading hess array (saves bandwidth) -): - """Phase 6.3: Shared memory histogram with 100x fewer global atomics. - - Key insight: Use shared memory for local histogram building (fast ~5 cycle atomics), - then reduce to global memory (only 512 atomics per block instead of 8K). - - Grid: (n_features, n_sample_chunks) - Block: 256 threads - - Shared memory: 16 nodes × 256 bins × 2 values = 32KB (fits in 48KB limit) - For depth 5 (32 nodes), we do two passes (nodes 0-15, then 16-31). - """ - feature_idx = cuda.blockIdx.x - chunk_idx = cuda.blockIdx.y - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory: 16 nodes × 256 bins × 2 (grad, hess) = 32KB - # Using flat array to avoid Numba 3D shared array issues - # Layout: [node * 512 + bin * 2 + 0/1] - local_hist = cuda.shared.array(16 * 256 * 2, dtype=float32) - - # === Phase 1: Initialize shared memory === - # 16 * 256 * 2 = 8192 elements, 256 threads → 32 elements per thread - for i in range(thread_idx, 16 * 256 * 2, block_size): - local_hist[i] = float32(0.0) - cuda.syncthreads() - - # === Phase 2: Build local histogram (FAST local atomics ~5 cycles) === - CHUNK_SIZE = 32768 - chunk_start = chunk_idx * CHUNK_SIZE - chunk_end = chunk_start + CHUNK_SIZE - if chunk_end > n_samples: - chunk_end = n_samples - - for sample_idx in range(chunk_start + thread_idx, chunk_end, block_size): - node_id = sample_node_ids[sample_idx] - local_node = node_id - level_start - node_offset - - # Only process if sample belongs to nodes in this pass - if 0 <= local_node < n_nodes_in_pass: - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = const_hess if const_hess > float32(0.0) else hess[sample_idx] - # Local atomics to shared memory (~5 cycles vs ~15 for global) - hist_idx_grad = local_node * 512 + bin_val * 2 + 0 - hist_idx_hess = local_node * 512 + bin_val * 2 + 1 - cuda.atomic.add(local_hist, hist_idx_grad, g) - cuda.atomic.add(local_hist, hist_idx_hess, h) - - cuda.syncthreads() - - # === Phase 3: Reduce to global (only ~512 atomics per block per node) === - # Each thread handles multiple bins across multiple nodes - for local_node in range(n_nodes_in_pass): - global_node = level_start + node_offset + local_node - for bin_idx in range(thread_idx, 256, block_size): - hist_idx_grad = local_node * 512 + bin_idx * 2 + 0 - hist_idx_hess = local_node * 512 + bin_idx * 2 + 1 - val_grad = local_hist[hist_idx_grad] - val_hess = local_hist[hist_idx_hess] - # Only write non-zero values to reduce atomic contention - if val_grad != float32(0.0): - cuda.atomic.add(global_histograms, (global_node, feature_idx, bin_idx, 0), val_grad) - if val_hess != float32(0.0): - cuda.atomic.add(global_histograms, (global_node, feature_idx, bin_idx, 1), val_hess) - - -@cuda.jit -def _build_histogram_left_only_shared_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - level_start, # int32 - first node at current level - n_left_in_pass, # int32 - how many left children in this pass (<=16) - left_offset, # int32 - offset among left children for multi-pass - global_histograms,# (max_nodes, n_features, 256, 2) float32 - output - const_hess, # float32 - >0: use this instead of reading hess array (saves bandwidth) -): - """Shared memory histogram for LEFT children only (histogram subtraction). - - Left children occupy even positions (0, 2, 4, ...) within the level. - Building only left children halves passes at each depth; right children - are computed via subtraction (parent - left). - - Grid: (n_features, n_sample_chunks) - Block: 256 threads - """ - feature_idx = cuda.blockIdx.x - chunk_idx = cuda.blockIdx.y - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory: 16 left children × 256 bins × 2 (grad, hess) = 32KB - local_hist = cuda.shared.array(16 * 256 * 2, dtype=float32) - - # Initialize shared memory - for i in range(thread_idx, 16 * 256 * 2, block_size): - local_hist[i] = float32(0.0) - cuda.syncthreads() - - # Build local histogram for left children only - CHUNK_SIZE = 32768 - chunk_start = chunk_idx * CHUNK_SIZE - chunk_end = chunk_start + CHUNK_SIZE - if chunk_end > n_samples: - chunk_end = n_samples - - for sample_idx in range(chunk_start + thread_idx, chunk_end, block_size): - node_id = sample_node_ids[sample_idx] - relative = node_id - level_start - - # Only process left children (even positions within level) - if relative >= 0 and (relative & 1) == 0: - left_idx = relative >> 1 # index among left children - local_node = left_idx - left_offset - if 0 <= local_node < n_left_in_pass: - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = const_hess if const_hess > float32(0.0) else hess[sample_idx] - hist_idx_grad = local_node * 512 + bin_val * 2 + 0 - hist_idx_hess = local_node * 512 + bin_val * 2 + 1 - cuda.atomic.add(local_hist, hist_idx_grad, g) - cuda.atomic.add(local_hist, hist_idx_hess, h) - - cuda.syncthreads() - - # Reduce to global — map local_node back to actual node index - for local_node in range(n_left_in_pass): - global_node = level_start + (left_offset + local_node) * 2 # even pos - for bin_idx in range(thread_idx, 256, block_size): - hist_idx_grad = local_node * 512 + bin_idx * 2 + 0 - hist_idx_hess = local_node * 512 + bin_idx * 2 + 1 - val_grad = local_hist[hist_idx_grad] - val_hess = local_hist[hist_idx_hess] - if val_grad != float32(0.0): - cuda.atomic.add(global_histograms, (global_node, feature_idx, bin_idx, 0), val_grad) - if val_hess != float32(0.0): - cuda.atomic.add(global_histograms, (global_node, feature_idx, bin_idx, 1), val_hess) - - -@cuda.jit -def _build_histogram_left_global_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - level_start, # int32 - first node at current level - global_histograms,# (max_nodes, n_features, 256, 2) float32 - output - const_hess, # float32 - >0: use this instead of reading hess array -): - """Direct global-atomic histogram for ALL left children in one pass. - - Replaces multi-pass shared-memory approach at deep levels where - n_left_children > _NODES_PER_PASS. One pass over the data instead of - ceil(n_left / 16) passes eliminates redundant sample reads. - - Grid: (n_features, n_sample_chunks) - Block: 256 threads - """ - feature_idx = cuda.blockIdx.x - chunk_idx = cuda.blockIdx.y - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - CHUNK_SIZE = 32768 - chunk_start = chunk_idx * CHUNK_SIZE - chunk_end = chunk_start + CHUNK_SIZE - if chunk_end > n_samples: - chunk_end = n_samples - - for sample_idx in range(chunk_start + thread_idx, chunk_end, block_size): - node_id = sample_node_ids[sample_idx] - relative = node_id - level_start - - # Only process left children (even positions within level) - if relative >= 0 and (relative & 1) == 0: - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - cuda.atomic.add(global_histograms, (node_id, feature_idx, bin_val, 0), g) - if const_hess > float32(0.0): - cuda.atomic.add(global_histograms, (node_id, feature_idx, bin_val, 1), const_hess) - else: - h = hess[sample_idx] - cuda.atomic.add(global_histograms, (node_id, feature_idx, bin_val, 1), h) - - -@cuda.jit -def _build_left_children_histograms_kernel( - binned, # (n_features, n_samples) uint8 - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - which node each sample belongs to - parent_level_start, # int32 - first parent node index - parent_level_end, # int32 - last parent node index + 1 - node_features, # (max_nodes,) int32 - to check if parent split - histograms, # (max_nodes, n_features, 256, 2) float32 - output -): - """Build histograms for LEFT children only (Phase 5.4: histogram subtraction). - - For each parent at [parent_level_start, parent_level_end): - - Left child is at 2*parent + 1 - - Only build histogram if parent split (node_features[parent] >= 0) - - Thread layout: - - blockIdx.x = feature index - - blockIdx.y = parent offset within level - - threads cooperate to process all samples - """ - feature_idx = cuda.blockIdx.x - parent_offset = cuda.blockIdx.y - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - parent_idx = parent_level_start + parent_offset - if parent_idx >= parent_level_end: - return - - # Skip if parent didn't split - if node_features[parent_idx] < 0: - return - - # Left child index - left_child_idx = 2 * parent_idx + 1 - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory for local histogram - local_grad = cuda.shared.array(256, dtype=float32) - local_hess = cuda.shared.array(256, dtype=float32) - - # Initialize shared memory - for i in range(thread_idx, 256, block_size): - local_grad[i] = float32(0.0) - local_hess[i] = float32(0.0) - cuda.syncthreads() - - # Accumulate samples belonging to left child - for sample_idx in range(thread_idx, n_samples, block_size): - if sample_node_ids[sample_idx] == left_child_idx: - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = hess[sample_idx] - cuda.atomic.add(local_grad, bin_val, g) - cuda.atomic.add(local_hess, bin_val, h) - cuda.syncthreads() - - # Write to global memory - for i in range(thread_idx, 256, block_size): - histograms[left_child_idx, feature_idx, i, 0] = local_grad[i] - histograms[left_child_idx, feature_idx, i, 1] = local_hess[i] - - -@cuda.jit -def _subtract_histograms_for_right_children_kernel( - parent_level_start, # int32 - first parent node index - parent_level_end, # int32 - last parent node index + 1 - node_features, # (max_nodes,) int32 - to check if parent split - histograms, # (max_nodes, n_features, 256, 2) float32 - in/out - n_features, # int32 -): - """Compute RIGHT child histogram = parent - left (Phase 5.4). - - Thread layout: - - blockIdx.x = parent offset within level - - threads handle (feature, bin) pairs - - For each parent that split: - - right_child = 2*parent + 2 - - left_child = 2*parent + 1 - - right_hist = parent_hist - left_hist - """ - parent_offset = cuda.blockIdx.x - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - parent_idx = parent_level_start + parent_offset - if parent_idx >= parent_level_end: - return - - # Skip if parent didn't split - if node_features[parent_idx] < 0: - return - - left_child_idx = 2 * parent_idx + 1 - right_child_idx = 2 * parent_idx + 2 - - # Each thread handles multiple (feature, bin) pairs - total_elements = n_features * 256 - for idx in range(thread_idx, total_elements, block_size): - feature = idx // 256 - bin_idx = idx % 256 - - # right = parent - left - parent_grad = histograms[parent_idx, feature, bin_idx, 0] - parent_hess = histograms[parent_idx, feature, bin_idx, 1] - left_grad = histograms[left_child_idx, feature, bin_idx, 0] - left_hess = histograms[left_child_idx, feature, bin_idx, 1] - - histograms[right_child_idx, feature, bin_idx, 0] = parent_grad - left_grad - histograms[right_child_idx, feature, bin_idx, 1] = parent_hess - left_hess - - -@cuda.jit -def _find_level_splits_kernel( - histograms, # (max_nodes, n_features, 256, 2) float32 - level_start, # int32 - first node at this level - level_end, # int32 - last node + 1 at this level - reg_lambda, # float64 - min_child_weight, # float64 - min_gain, # float64 - # Outputs: - node_features, # (max_nodes,) int32 - best feature per node - node_thresholds, # (max_nodes,) int32 - best bin per node - node_gains, # (max_nodes,) float32 - best gain per node - node_sum_grad, # (max_nodes,) float64 - total grad per node - node_sum_hess, # (max_nodes,) float64 - total hess per node - node_left_hess, # (max_nodes,) float32 - hess going left at best split -): - """Find best split for each node at current level. - - Thread layout: - - blockIdx.x = node offset within level - - threads handle features in parallel, then reduce - Prefix sums and gain computation in float64 to match CPU precision. - """ - node_offset = cuda.blockIdx.x - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - node_idx = level_start + node_offset - if node_idx >= level_end: - return - - n_features = histograms.shape[1] - - # Shared memory for reduction (float32 for final results) - shared_gains = cuda.shared.array(256, dtype=float32) - shared_bins = cuda.shared.array(256, dtype=int32) - shared_features = cuda.shared.array(256, dtype=int32) - shared_left_hess = cuda.shared.array(256, dtype=float32) - - # Shared memory for broadcasting total_grad / total_hess from feature 0 - # so all features use the same values (avoids per-feature divergence - # from floating-point accumulation order differences). - # Use float64 for precision in the prefix sum. - shared_total = cuda.shared.array(2, dtype=float64) # [0]=grad, [1]=hess - - # Initialize - shared_gains[thread_idx] = float32(-1e30) - shared_bins[thread_idx] = int32(-1) - shared_features[thread_idx] = int32(-1) - shared_left_hess[thread_idx] = float32(0.0) - - # Step 1: Thread 0 computes total_grad/total_hess from feature 0's histogram - if thread_idx == 0: - tg = float64(0.0) - th = float64(0.0) - for b in range(256): - tg += float64(histograms[node_idx, 0, b, 0]) - th += float64(histograms[node_idx, 0, b, 1]) - shared_total[0] = tg - shared_total[1] = th - node_sum_grad[node_idx] = tg - node_sum_hess[node_idx] = th - cuda.syncthreads() - - # All threads read the same total values (float64) - total_grad = shared_total[0] - total_hess = shared_total[1] - - # Parent gain in float64 - reg_lambda_64 = float64(reg_lambda) - min_cw_64 = float64(min_child_weight) - parent_gain = total_grad * total_grad / (total_hess + reg_lambda_64) - - # Each thread handles multiple features - for feature_idx in range(thread_idx, n_features, block_size): - # Scan bins to find best split for this feature (float64 prefix sums) - left_grad = float64(0.0) - left_hess = float64(0.0) - best_bin = int32(-1) - best_gain = float64(-1e30) - best_left_hess = float64(0.0) - - for bin_idx in range(255): - left_grad += float64(histograms[node_idx, feature_idx, bin_idx, 0]) - left_hess += float64(histograms[node_idx, feature_idx, bin_idx, 1]) - right_grad = total_grad - left_grad - right_hess = total_hess - left_hess - - if left_hess >= min_cw_64 and right_hess >= min_cw_64: - left_score = left_grad * left_grad / (left_hess + reg_lambda_64) - right_score = right_grad * right_grad / (right_hess + reg_lambda_64) - gain = left_score + right_score - parent_gain - - if gain > best_gain: - best_gain = gain - best_bin = bin_idx - best_left_hess = left_hess - - # Update shared memory if this feature is better - if best_gain > float64(shared_gains[thread_idx]): - shared_gains[thread_idx] = float32(best_gain) - shared_bins[thread_idx] = best_bin - shared_features[thread_idx] = feature_idx - shared_left_hess[thread_idx] = float32(best_left_hess) - - cuda.syncthreads() - - # Tree reduction to find global best - s = block_size // 2 - while s > 0: - if thread_idx < s and shared_gains[thread_idx + s] > shared_gains[thread_idx]: - shared_gains[thread_idx] = shared_gains[thread_idx + s] - shared_bins[thread_idx] = shared_bins[thread_idx + s] - shared_features[thread_idx] = shared_features[thread_idx + s] - shared_left_hess[thread_idx] = shared_left_hess[thread_idx + s] - cuda.syncthreads() - s //= 2 - - # Thread 0 writes final result - if thread_idx == 0: - if shared_gains[0] > min_gain: - node_features[node_idx] = shared_features[0] - node_thresholds[node_idx] = shared_bins[0] - node_gains[node_idx] = shared_gains[0] - node_left_hess[node_idx] = shared_left_hess[0] - else: - node_features[node_idx] = int32(-1) # Leaf - node_thresholds[node_idx] = int32(-1) - node_gains[node_idx] = float32(-1e30) - node_left_hess[node_idx] = float32(0.0) - - -@cuda.jit -def _create_children_kernel( - level_start, # int32 - first node at this level - level_end, # int32 - last node + 1 at this level - node_features, # (max_nodes,) int32 - best feature (-1 = leaf) - node_left, # (max_nodes,) int32 - output: left child idx - node_right, # (max_nodes,) int32 - output: right child idx -): - """Create child node indices for all nodes at this level. - - For a complete binary tree: - - Node i's left child is at 2*i + 1 - - Node i's right child is at 2*i + 2 - """ - node_offset = cuda.grid(1) - node_idx = level_start + node_offset - - if node_idx >= level_end: - return - - if node_features[node_idx] >= 0: - # Internal node - set children - node_left[node_idx] = 2 * node_idx + 1 - node_right[node_idx] = 2 * node_idx + 2 - else: - # Leaf node - no children - node_left[node_idx] = int32(-1) - node_right[node_idx] = int32(-1) - - -@cuda.jit -def _swap_children_for_smaller_child_kernel( - level_start, # int32 - level_end, # int32 - node_features, # (max_nodes,) int32 - node_left_hess, # (max_nodes,) float32 — hess going left at best split - node_sum_hess, # (max_nodes,) float32 — total hess per node - node_left, # (max_nodes,) int32 — swapped in-place - node_right, # (max_nodes,) int32 — swapped in-place -): - """Swap left/right children so the SMALLER child is at the even position. - - The left-only histogram kernel always builds even-position children - (2*parent+1). By swapping node_left/node_right when left is the larger - child, the partition kernel sends fewer samples to the even position, - reducing histogram atomic contention and memory reads. - - Prediction stays correct because node_left/node_right always point to - the side matching ``binned <= threshold`` / ``binned > threshold``. - """ - node_offset = cuda.grid(1) - node_idx = level_start + node_offset - if node_idx >= level_end: - return - if node_features[node_idx] < 0: - return # leaf — no children - - left_h = node_left_hess[node_idx] - right_h = node_sum_hess[node_idx] - left_h - - if left_h > right_h: - # Left child has more samples → swap so smaller goes to even pos - tmp = node_left[node_idx] - node_left[node_idx] = node_right[node_idx] - node_right[node_idx] = tmp - - -@cuda.jit -def _partition_samples_kernel( - binned, # (n_features, n_samples) uint8 - sample_node_ids, # (n_samples,) int32 - input/output - node_features, # (max_nodes,) int32 - split feature (-1 = leaf) - node_thresholds, # (max_nodes,) int32 - split bin - node_left, # (max_nodes,) int32 - left child idx - node_right, # (max_nodes,) int32 - right child idx - level_start, # int32 - first node at this level - level_end, # int32 - last node + 1 at this level -): - """Partition samples: move each sample to its new node (left or right child). - - Each thread handles one sample. - """ - sample_idx = cuda.grid(1) - n_samples = sample_node_ids.shape[0] - - if sample_idx >= n_samples: - return - - node_idx = sample_node_ids[sample_idx] - - # Only process if sample is at current level - if node_idx < level_start or node_idx >= level_end: - return - - feature = node_features[node_idx] - - if feature < 0: - # Node is a leaf, sample stays - return - - threshold = node_thresholds[node_idx] - sample_bin = int32(binned[feature, sample_idx]) - - if sample_bin <= threshold: - sample_node_ids[sample_idx] = node_left[node_idx] - else: - sample_node_ids[sample_idx] = node_right[node_idx] - - -@cuda.jit -def _partition_samples_inline_kernel( - binned, # (n_features, n_samples) uint8 - sample_node_ids, # (n_samples,) int32 - input/output - node_features, # (max_nodes,) int32 - split feature (-1 = leaf) - node_thresholds, # (max_nodes,) int32 - split bin - node_left_hess, # (max_nodes,) float32 - hess going left at best split - node_sum_hess, # (max_nodes,) float64 - total hess per node - level_start, # int32 - first node at this level - level_end, # int32 - last node + 1 at this level -): - """Partition samples with inline children computation. - - Computes left/right child indices on the fly (2*i+1, 2*i+2) with the - smaller-child swap, eliminating separate create_children and - swap_children kernel launches per depth. - """ - sample_idx = cuda.grid(1) - n_samples = sample_node_ids.shape[0] - - if sample_idx >= n_samples: - return - - node_idx = sample_node_ids[sample_idx] - - if node_idx < level_start or node_idx >= level_end: - return - - feature = node_features[node_idx] - - if feature < 0: - return - - threshold = node_thresholds[node_idx] - sample_bin = int32(binned[feature, sample_idx]) - - # Compute children inline - left_child = 2 * node_idx + 1 - right_child = 2 * node_idx + 2 - - # Smaller-child swap: put smaller child at even position - left_h = float64(node_left_hess[node_idx]) - if left_h > node_sum_hess[node_idx] - left_h: - left_child = 2 * node_idx + 2 - right_child = 2 * node_idx + 1 - - if sample_bin <= threshold: - sample_node_ids[sample_idx] = left_child - else: - sample_node_ids[sample_idx] = right_child - - -@cuda.jit -def _create_all_children_kernel( - node_features, # (max_nodes,) int32 - node_left_hess, # (max_nodes,) float32 - node_sum_hess, # (max_nodes,) float64 - node_left, # (max_nodes,) int32 - output - node_right, # (max_nodes,) int32 - output - max_nodes, # int32 -): - """Batch-create child indices for all internal nodes after the depth loop. - - Combines create_children + swap_children into a single post-loop pass. - """ - node_idx = cuda.grid(1) - if node_idx >= max_nodes: - return - - if node_features[node_idx] < 0: - node_left[node_idx] = int32(-1) - node_right[node_idx] = int32(-1) - return - - left = 2 * node_idx + 1 - right = 2 * node_idx + 2 - - left_h = float64(node_left_hess[node_idx]) - if left_h > node_sum_hess[node_idx] - left_h: - left, right = right, left - - node_left[node_idx] = left - node_right[node_idx] = right - - -@cuda.jit -def _compute_leaf_sums_kernel( - grad, # (n_samples,) float32 - hess, # (n_samples,) float32 - sample_node_ids, # (n_samples,) int32 - final node for each sample - node_sum_grad, # (max_nodes,) float64 - output (atomic add) - node_sum_hess, # (max_nodes,) float64 - output (atomic add) -): - """Compute sum of grad/hess for each leaf node from samples. - - This is needed for leaves at max_depth that never went through - _find_level_splits_kernel. Uses atomic adds since multiple samples - may belong to the same leaf. - """ - sample_idx = cuda.grid(1) - n_samples = grad.shape[0] - - if sample_idx >= n_samples: - return - - node_idx = sample_node_ids[sample_idx] - # Promote float32 grad/hess to float64 for accumulation precision. - # NOTE: float64 atomic adds reduce but don't eliminate non-determinism - # from thread execution order; rounding errors are ~1e-15 vs ~1e-7. - cuda.atomic.add(node_sum_grad, node_idx, float64(grad[sample_idx])) - cuda.atomic.add(node_sum_hess, node_idx, float64(hess[sample_idx])) - - -@cuda.jit -def _compute_leaf_values_kernel( - node_features, # (max_nodes,) int32 - -1 for leaves - node_sum_grad, # (max_nodes,) float64 - node_sum_hess, # (max_nodes,) float64 - reg_lambda, # float64 - node_values, # (max_nodes,) float32 - output - max_nodes, # int32 -): - """Compute leaf values for all leaf nodes. - - Reads float64 sums, computes in float64, outputs float32 leaf values. - """ - node_idx = cuda.grid(1) - - if node_idx >= max_nodes: - return - - if node_features[node_idx] < 0: - # Leaf node: value = -sum_grad / (sum_hess + lambda) - sum_grad = node_sum_grad[node_idx] - sum_hess = node_sum_hess[node_idx] - if sum_hess + reg_lambda > 0.0: - node_values[node_idx] = float32(-sum_grad / (sum_hess + reg_lambda)) - else: - node_values[node_idx] = float32(0.0) - else: - node_values[node_idx] = float32(0.0) - - -# NOTE: Phase 7 (row-based partitioning) code was removed after analysis. -# V1's sample_node_ids approach is 4x faster than row-based in Python/Numba. -# See logs/2026-01-03-phase-7-final.md for details. - - -@cuda.jit -def _derive_leaf_sums_from_histogram( - histograms, # (max_nodes, n_features, 256, 2) float32 - node_features, # (max_nodes,) int32 — split feature per node - node_thresholds, # (max_nodes,) int32 — split threshold per node - node_left, # (max_nodes,) int32 — left child idx - node_right, # (max_nodes,) int32 — right child idx - node_sum_grad, # (max_nodes,) float64 — output for children - node_sum_hess, # (max_nodes,) float64 — output for children - last_internal_start, # int32 — first node at depth max_depth-1 - last_internal_end, # int32 — first node at depth max_depth -): - """Derive grad/hess sums for final-depth leaves from parent histograms. - - The split finding kernel already sets node_sum_grad/hess for all nodes - at depths 0..max_depth-1. Only the children at depth max_depth are - missing. For each parent at depth max_depth-1 that split, compute - children sums via a prefix sum over the parent's histogram for the - split feature. This replaces the O(n_samples) atomic-add scan. - """ - node_offset = cuda.grid(1) - parent = last_internal_start + node_offset - if parent >= last_internal_end: - return - - feature = node_features[parent] - if feature < 0: - return # Parent is a leaf — no children to compute - - threshold = node_thresholds[parent] - - # Prefix sum over parent histogram for split feature → left child sums - left_g = float64(0.0) - left_h = float64(0.0) - for b in range(threshold + 1): - left_g += histograms[parent, feature, b, 0] - left_h += histograms[parent, feature, b, 1] - - # Right child = parent total − left - total_g = node_sum_grad[parent] - total_h = node_sum_hess[parent] - - left_child = node_left[parent] - right_child = node_right[parent] - node_sum_grad[left_child] = left_g - node_sum_hess[left_child] = left_h - node_sum_grad[right_child] = total_g - left_g - node_sum_hess[right_child] = total_h - left_h - - -# ============================================================================= -# Fast MSE gradient kernel: writes to pre-allocated arrays, skips hessian -# (MSE hessian is constant 1.0 for L=0.5*(pred-y)^2). Eliminates cudaMalloc/cudaFree overhead -# that _mse_gradient_gpu incurs every iteration. -# ============================================================================= - -@cuda.jit -def _mse_grad_inplace_kernel(pred, y, grad, n): - """Compute MSE gradient in-place: grad = pred - y. - - Hessian is skipped — caller pre-fills it once with 1.0. - """ - idx = cuda.grid(1) - if idx < n: - grad[idx] = pred[idx] - y[idx] - - -def mse_grad_inplace_gpu(pred, y, grad): - """Compute MSE gradient into pre-allocated grad array (zero allocation). - - Args: - pred: Device array (n_samples,) float32 — current predictions - y: Device array (n_samples,) float32 — targets - grad: Device array (n_samples,) float32 — output (written in-place) - """ - n = pred.shape[0] - threads = 256 - blocks = (n + threads - 1) // threads - _mse_grad_inplace_kernel[blocks, threads](pred, y, grad, n) - - -# ============================================================================= -# Fast LogLoss gradient kernel: writes to pre-allocated arrays, eliminating -# cudaMalloc/cudaFree overhead that _logloss_gradient_gpu incurs every -# iteration (~0.5-2ms per alloc × 2 arrays × 200 trees = significant). -# ============================================================================= - -@cuda.jit -def _logloss_grad_inplace_kernel(pred, y, grad, hess, n): - """Compute logloss gradient and hessian in-place. - - grad = sigmoid(pred) - y - hess = sigmoid(pred) * (1 - sigmoid(pred)), clipped to [1e-6, 1-1e-6] - """ - idx = cuda.grid(1) - if idx < n: - x = pred[idx] - # Numerically stable sigmoid - if x >= 0.0: - p = 1.0 / (1.0 + math.exp(-x)) - else: - exp_x = math.exp(x) - p = exp_x / (1.0 + exp_x) - grad[idx] = float32(p - y[idx]) - h = p * (1.0 - p) - if h < 1e-6: - h = 1e-6 - elif h > 1.0 - 1e-6: - h = 1.0 - 1e-6 - hess[idx] = float32(h) - - -def logloss_grad_inplace_gpu(pred, y, grad, hess): - """Compute logloss gradient into pre-allocated arrays (zero allocation). - - Args: - pred: Device array (n_samples,) float32 - current predictions (log-odds) - y: Device array (n_samples,) float32 - targets (0/1) - grad: Device array (n_samples,) float32 - output gradient (in-place) - hess: Device array (n_samples,) float32 - output hessian (in-place) - """ - n = pred.shape[0] - threads = 256 - blocks = (n + threads - 1) // threads - _logloss_grad_inplace_kernel[blocks, threads](pred, y, grad, hess, n) - - -# ============================================================================= -# GPU-native profiling hook: set by ProfilingCallback to collect per-phase -# timings inside build_tree_gpu_native (which bypasses the shared primitives). -# When None, no profiling overhead is added. -# ============================================================================= -_gpu_profile_timers: dict | None = None - - -# ============================================================================= -# Workspace cache: avoids repeated cudaMalloc for large arrays (histograms -# ~100MB, sample_node_ids ~4MB) across 200+ tree builds per training run. -# ============================================================================= -_tree_workspace_cache: dict | None = None - - -def clear_tree_workspace_cache() -> None: - """Free cached GPU workspace arrays. - - Call this between training runs with different data shapes to avoid - accumulating stale GPU allocations. - """ - global _tree_workspace_cache - _tree_workspace_cache = None - - -@cuda.jit -def _predict_from_node_ids_kernel( - sample_node_ids, # (n_samples,) int32 - leaf node for each sample - node_values, # (max_nodes,) float32 - leaf values - pred, # (n_samples,) float32 - predictions to update - learning_rate, # float32 - n_samples, # int32 -): - """Fused prediction: pred[i] += lr * node_values[sample_node_ids[i]]. - - Avoids a separate tree traversal by using already-computed sample-to-leaf - mapping. Only 2 memory reads per sample vs O(depth) for tree traversal. - """ - idx = cuda.grid(1) - if idx < n_samples: - pred[idx] += learning_rate * node_values[sample_node_ids[idx]] - - -def _get_tree_workspace(n_samples: int, n_features: int, max_depth: int) -> dict: - """Return cached workspace arrays, allocating only on first call or shape change.""" - global _tree_workspace_cache - key = (n_samples, n_features, max_depth) - if _tree_workspace_cache is not None and _tree_workspace_cache.get('_key') == key: - return _tree_workspace_cache - max_nodes = 2**(max_depth + 1) - 1 - _tree_workspace_cache = { - '_key': key, - 'sample_node_ids': cuda.device_array(n_samples, dtype=np.int32), - 'histograms': cuda.device_array((max_nodes, n_features, 256, 2), dtype=np.float32), - 'node_gains': cuda.device_array(max_nodes, dtype=np.float32), - 'node_sum_grad': cuda.device_array(max_nodes, dtype=np.float64), - 'node_sum_hess': cuda.device_array(max_nodes, dtype=np.float64), - 'node_left_hess': cuda.device_array(max_nodes, dtype=np.float32), - # Output arrays reused across tree builds to avoid cudaMalloc overhead - # (~2.5ms per cudaMalloc × 5 arrays × 200 trees = 2.5s saved) - 'out_features': cuda.device_array(max_nodes, dtype=np.int32), - 'out_thresholds': cuda.device_array(max_nodes, dtype=np.int32), - 'out_values': cuda.device_array(max_nodes, dtype=np.float32), - 'out_left': cuda.device_array(max_nodes, dtype=np.int32), - 'out_right': cuda.device_array(max_nodes, dtype=np.int32), - } - return _tree_workspace_cache - - -def build_tree_gpu_native( - binned: DeviceNDArray, - grad: DeviceNDArray, - hess: DeviceNDArray, - max_depth: int = 6, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - min_gain: float = 0.0, - pred_gpu: DeviceNDArray | None = None, - learning_rate: float = 0.0, - const_hess: float = 0.0, -) -> tuple[DeviceNDArray, DeviceNDArray, DeviceNDArray, DeviceNDArray, DeviceNDArray]: - """Build a tree entirely on GPU with minimal Python orchestration. - - Phase 3.2: O(depth) kernel launches instead of O(nodes). - Histograms in float32 for fast atomics; split finding uses float64 prefix - sums for precision; leaf values in float32. - ZERO copy_to_host() during building. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples), uint8 - grad: Gradient vector, shape (n_samples,), float32 - hess: Hessian vector, shape (n_samples,), float32 - max_depth: Maximum tree depth - reg_lambda: L2 regularization - min_child_weight: Minimum hessian sum in child - min_gain: Minimum gain to split - pred_gpu: If provided, fuse prediction update into leaf computation - (avoids separate tree traversal). - learning_rate: Scale factor for fused prediction update. - - Returns: - node_features: (max_nodes,) int32 - split feature (-1 = leaf) - node_thresholds: (max_nodes,) int32 - split bin - node_values: (max_nodes,) float32 - leaf values - node_left: (max_nodes,) int32 - left child index - node_right: (max_nodes,) int32 - right child index - - Note: - Returned arrays are aliased to a shared workspace cache for - performance. A subsequent call with the same (n_samples, n_features, - max_depth) will overwrite them. Callers must copy data out (D2D or - copy_to_host) before the next call. - """ - n_features, n_samples = binned.shape - max_nodes = 2**(max_depth + 1) - 1 - - # Reuse workspace arrays (histograms ~100MB, sample_node_ids ~4MB) - # to avoid cudaMalloc/cudaFree overhead across 200+ tree builds. - ws = _get_tree_workspace(n_samples, n_features, max_depth) - sample_node_ids = ws['sample_node_ids'] - histograms = ws['histograms'] - node_gains = ws['node_gains'] - node_left_hess = ws['node_left_hess'] - node_sum_grad = ws['node_sum_grad'] - node_sum_hess = ws['node_sum_hess'] - - # Reuse output arrays from workspace to avoid cudaMalloc overhead. - # Caller must copy data out before the next call overwrites them. - node_features = ws['out_features'] - node_thresholds = ws['out_thresholds'] - node_values = ws['out_values'] - node_left = ws['out_left'] - node_right = ws['out_right'] - - # Initialize all nodes as leaves (Phase 3.6: use module-level kernel) - threads = 256 - blocks = math.ceil(max_nodes / threads) - _init_tree_nodes_kernel[blocks, threads](node_features, node_thresholds, node_left, node_right, max_nodes) - - # Initialize all samples to root node (node 0) - sample_blocks = math.ceil(n_samples / threads) - _init_sample_nodes_kernel[sample_blocks, threads](sample_node_ids, n_samples) - - # Convert parameters to float64 for kernel precision - reg_lambda_f64 = np.float64(reg_lambda) - min_child_weight_f64 = np.float64(min_child_weight) - min_gain_f64 = np.float64(min_gain) - - # Profiling hook: when _gpu_profile_timers is set, record per-phase times - _prof = _gpu_profile_timers - - # Build tree level by level - CHUNK_SIZE = 32768 - n_chunks = math.ceil(n_samples / CHUNK_SIZE) - hist_grid = (n_features, n_chunks) - _NODES_PER_PASS = 16 - const_hess_f32 = np.float32(const_hess) - - for depth in range(max_depth): - level_start = 2**depth - 1 # First node at this depth - level_end = 2**(depth + 1) - 1 # First node at next depth - n_nodes_at_level = level_end - level_start - - # --- Histogram building --- - if _prof is not None: - cuda.synchronize() - _t0 = _time.perf_counter() - - # Zero histograms for this level - zero_grid = (n_nodes_at_level, n_features) - _zero_level_histograms_kernel[zero_grid, 256]( - histograms, level_start, level_end, n_features - ) - - if depth == 0: - # Root: build from scratch (1 node, 1 pass) - _build_histogram_shared_kernel[hist_grid, 256]( - binned, grad, hess, sample_node_ids, - level_start, 1, 0, histograms, const_hess_f32 - ) - else: - # Histogram subtraction: build LEFT children only, then - # right = parent - left. Halves passes at each depth. - n_left_children = n_nodes_at_level // 2 - if n_left_children > _NODES_PER_PASS: - # Deep level: single-pass global atomics instead of - # multi-pass shared-memory. Eliminates redundant reads - # of all samples on each extra pass. - _build_histogram_left_global_kernel[hist_grid, 256]( - binned, grad, hess, sample_node_ids, - level_start, histograms, const_hess_f32 - ) - else: - n_passes = (n_left_children + _NODES_PER_PASS - 1) // _NODES_PER_PASS - for pass_idx in range(n_passes): - left_offset = pass_idx * _NODES_PER_PASS - n_left_this_pass = min(_NODES_PER_PASS, n_left_children - left_offset) - _build_histogram_left_only_shared_kernel[hist_grid, 256]( - binned, grad, hess, sample_node_ids, - level_start, n_left_this_pass, left_offset, - histograms, const_hess_f32 - ) - # Subtract parent - left = right for all right children - parent_level_start = 2**(depth - 1) - 1 - parent_level_end = 2**depth - 1 - n_parents = parent_level_end - parent_level_start - _subtract_histograms_for_right_children_kernel[n_parents, 256]( - parent_level_start, parent_level_end, - node_features, histograms, n_features - ) - - if _prof is not None: - cuda.synchronize() - _prof['histogram_build'] += _time.perf_counter() - _t0 - - # --- Split finding --- - if _prof is not None: - _t0 = _time.perf_counter() - - split_grid = n_nodes_at_level - split_block = 256 - _find_level_splits_kernel[split_grid, split_block]( - histograms, level_start, level_end, - reg_lambda_f64, min_child_weight_f64, min_gain_f64, - node_features, node_thresholds, node_gains, - node_sum_grad, node_sum_hess, node_left_hess - ) - - if _prof is not None: - cuda.synchronize() - _prof['split_find'] += _time.perf_counter() - _t0 - - # --- Partition --- - if _prof is not None: - _t0 = _time.perf_counter() - - # Fused partition: compute children inline (2*i+1, 2*i+2) with - # smaller-child swap, eliminating 2 kernel launches per depth. - _partition_samples_inline_kernel[sample_blocks, threads]( - binned, sample_node_ids, node_features, node_thresholds, - node_left_hess, node_sum_hess, level_start, level_end - ) - - if _prof is not None: - cuda.synchronize() - _prof['partition'] += _time.perf_counter() - _t0 - - # --- Leaf computation --- - if _prof is not None: - cuda.synchronize() - _t0 = _time.perf_counter() - - # Batch-create all children after the depth loop (replaces per-depth - # create_children + swap_children kernels, saving 2 launches × depth). - _create_all_children_kernel[blocks, threads]( - node_features, node_left_hess, node_sum_hess, - node_left, node_right, max_nodes - ) - - # node_sum_grad/hess already set for depths 0..max_depth-1 by split - # finding. Only depth-max_depth children (the deepest leaves) need - # their sums derived — do that from the parent's histogram rather than - # an expensive O(n_samples) atomic scan. - last_internal_start = 2**(max_depth - 1) - 1 - last_internal_end = 2**max_depth - 1 - n_last_internal = last_internal_end - last_internal_start - if n_last_internal > 0: - last_blocks = math.ceil(n_last_internal / threads) - _derive_leaf_sums_from_histogram[last_blocks, threads]( - histograms, node_features, node_thresholds, - node_left, node_right, - node_sum_grad, node_sum_hess, - last_internal_start, last_internal_end, - ) - _compute_leaf_values_kernel[blocks, threads]( - node_features, node_sum_grad, node_sum_hess, - reg_lambda_f64, node_values, max_nodes - ) - - # Fused prediction: use sample_node_ids to update predictions directly, - # avoiding a separate tree traversal (saves O(n_samples × depth) reads). - if pred_gpu is not None: - _predict_from_node_ids_kernel[sample_blocks, threads]( - sample_node_ids, node_values, pred_gpu, - np.float32(learning_rate), n_samples, - ) - - if _prof is not None: - cuda.synchronize() - _prof['leaf_values'] += _time.perf_counter() - _t0 - - return node_features, node_thresholds, node_values, node_left, node_right - - -# ============================================================================= -# Phase 3.4: Symmetric (Oblivious) Tree GPU Implementation -# ============================================================================= - -@cuda.jit -def _predict_symmetric_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - level_features: DeviceNDArray, # (max_depth,) int32 - level_thresholds: DeviceNDArray, # (max_depth,) uint8 - leaf_values: DeviceNDArray, # (2^max_depth,) float32 - max_depth: int32, - predictions: DeviceNDArray, # (n_samples,) float32 -): - """Predict using symmetric tree - just bit operations! - - Each thread handles one sample. - """ - sample_idx = cuda.grid(1) - n_samples = binned.shape[1] - - if sample_idx >= n_samples: - return - - leaf_idx = int32(0) - - for depth in range(max_depth): - feature = level_features[depth] - if feature < 0: - break - threshold = level_thresholds[depth] - bin_value = binned[feature, sample_idx] - - # goes_right = bin_value > threshold - leaf_idx = 2 * leaf_idx + (1 if bin_value > threshold else 0) - - predictions[sample_idx] = leaf_values[leaf_idx] - - -def predict_symmetric_cuda( - binned: DeviceNDArray, - level_features: np.ndarray, - level_thresholds: np.ndarray, - leaf_values: np.ndarray, - max_depth: int, -) -> DeviceNDArray: - """GPU prediction for symmetric trees.""" - n_samples = binned.shape[1] - - # Transfer tree structure to GPU - level_features_gpu = cuda.to_device(level_features) - level_thresholds_gpu = cuda.to_device(level_thresholds) - leaf_values_gpu = cuda.to_device(leaf_values) - - predictions = cuda.device_array(n_samples, dtype=np.float32) - - threads = 256 - blocks = math.ceil(n_samples / threads) - - _predict_symmetric_kernel[blocks, threads]( - binned, level_features_gpu, level_thresholds_gpu, - leaf_values_gpu, max_depth, predictions - ) - - return predictions - - -@cuda.jit -def _build_symmetric_histogram_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - grad: DeviceNDArray, # (n_samples,) float32 - hess: DeviceNDArray, # (n_samples,) float32 - hist_grad: DeviceNDArray, # (n_features, 256) float32 - output - hist_hess: DeviceNDArray, # (n_features, 256) float32 - output -): - """Build GLOBAL histogram for symmetric tree. - - KNOWN DEFECT (CRIT-5): this kernel is NOT leaf-aware. It accumulates one - histogram over ALL samples and never consumes ``sample_leaf_ids``, so at - every depth ``build_tree_symmetric_gpu_native`` recomputes the identical - root histogram and selects the same split again — the resulting tree is - degenerate (one split repeated per level). A correct oblivious-tree level - split must aggregate PER-LEAF gains: accumulate one histogram per leaf - (indexed by sample_leaf_ids) and sum each candidate's gain across leaves, - as the CPU ``SymmetricGrowth._find_level_split`` does. Until then the - callers gate this path behind OPENBOOST_EXPERIMENTAL_SYMMETRIC_GPU=1. - - Thread layout: - - blockIdx.x = feature index - - threads cooperate to process all samples - """ - feature_idx = cuda.blockIdx.x - thread_idx = cuda.threadIdx.x - block_size = cuda.blockDim.x - - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if feature_idx >= n_features: - return - - # Shared memory for local histogram - local_grad = cuda.shared.array(256, dtype=float32) - local_hess = cuda.shared.array(256, dtype=float32) - - # Initialize - for i in range(thread_idx, 256, block_size): - local_grad[i] = float32(0.0) - local_hess[i] = float32(0.0) - cuda.syncthreads() - - # Accumulate ALL samples - for sample_idx in range(thread_idx, n_samples, block_size): - bin_val = int32(binned[feature_idx, sample_idx]) - g = grad[sample_idx] - h = hess[sample_idx] - cuda.atomic.add(local_grad, bin_val, g) - cuda.atomic.add(local_hess, bin_val, h) - cuda.syncthreads() - - # Write to global memory - for i in range(thread_idx, 256, block_size): - hist_grad[feature_idx, i] = local_grad[i] - hist_hess[feature_idx, i] = local_hess[i] - - -# Note: Batched symmetric kernels were removed because GBDT trees are sequential -# (each tree depends on previous tree's predictions). See: -# logs/2026-01-03-phase-3.4-symmetric-trees.md for details. - - -@cuda.jit -def _find_symmetric_split_kernel( - hist_grad: DeviceNDArray, # (n_features, 256) float32 - hist_hess: DeviceNDArray, # (n_features, 256) float32 - total_grad: float32, - total_hess: float32, - reg_lambda: float32, - min_child_weight: float32, - per_feature_gains: DeviceNDArray, # (n_features,) float32 - per_feature_thresholds: DeviceNDArray, # (n_features,) int32 -): - """Find best split per feature using parallel prefix sum. - - Phase 3.4: Uses Kogge-Stone scan for O(log n) cumsum instead of O(n²). - """ - feature_idx = cuda.blockIdx.x - tid = cuda.threadIdx.x - - n_features = hist_grad.shape[0] - - if feature_idx >= n_features: - return - - # Shared memory for prefix sums and reduction - cumsum_grad = cuda.shared.array(256, dtype=float32) - cumsum_hess = cuda.shared.array(256, dtype=float32) - shared_gains = cuda.shared.array(256, dtype=float32) - shared_thresholds = cuda.shared.array(256, dtype=int32) - - # Load histogram into shared memory - cumsum_grad[tid] = hist_grad[feature_idx, tid] - cumsum_hess[tid] = hist_hess[feature_idx, tid] - cuda.syncthreads() - - # Kogge-Stone inclusive scan - O(n log n) work, O(log n) steps - offset = 1 - while offset < 256: - temp_g = float32(0.0) - temp_h = float32(0.0) - if tid >= offset: - temp_g = cumsum_grad[tid - offset] - temp_h = cumsum_hess[tid - offset] - cuda.syncthreads() - if tid >= offset: - cumsum_grad[tid] = cumsum_grad[tid] + temp_g - cumsum_hess[tid] = cumsum_hess[tid] + temp_h - cuda.syncthreads() - offset *= 2 - - # Initialize gain/threshold - shared_gains[tid] = float32(-1e10) - shared_thresholds[tid] = int32(-1) - - parent_gain = total_grad * total_grad / (total_hess + reg_lambda) - - # Each thread evaluates its threshold (0-254 are valid split points) - if tid < 255: - left_grad = cumsum_grad[tid] - left_hess = cumsum_hess[tid] - right_grad = total_grad - left_grad - right_hess = total_hess - left_hess - - if left_hess >= min_child_weight and right_hess >= min_child_weight: - gain = (left_grad * left_grad / (left_hess + reg_lambda) + - right_grad * right_grad / (right_hess + reg_lambda) - - parent_gain) - shared_gains[tid] = gain - shared_thresholds[tid] = tid - - cuda.syncthreads() - - # Parallel reduction to find max gain within this feature - stride = 128 - while stride > 0: - if tid < stride and shared_gains[tid + stride] > shared_gains[tid]: - shared_gains[tid] = shared_gains[tid + stride] - shared_thresholds[tid] = shared_thresholds[tid + stride] - cuda.syncthreads() - stride //= 2 - - # Thread 0 stores result for this feature - if tid == 0: - per_feature_gains[feature_idx] = shared_gains[0] - per_feature_thresholds[feature_idx] = shared_thresholds[0] - - -@cuda.jit -def _partition_symmetric_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - sample_leaf_ids: DeviceNDArray, # (n_samples,) int32 - input/output - level_features: DeviceNDArray, # (max_depth,) int32 - GPU array - level_thresholds: DeviceNDArray, # (max_depth,) int32 - GPU array - depth: int32, -): - """Partition all samples using split from GPU arrays (symmetric tree). - - New leaf_id = 2 * old_leaf_id + (1 if goes_right else 0) - """ - sample_idx = cuda.grid(1) - n_samples = sample_leaf_ids.shape[0] - - if sample_idx >= n_samples: - return - - split_feature = level_features[depth] - if split_feature < 0: - return # No valid split at this level - - split_threshold = level_thresholds[depth] - current_leaf = sample_leaf_ids[sample_idx] - bin_value = int32(binned[split_feature, sample_idx]) - goes_right = 1 if bin_value > split_threshold else 0 - sample_leaf_ids[sample_idx] = 2 * current_leaf + goes_right - - -@cuda.jit -def _partition_symmetric_scalar_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - sample_leaf_ids: DeviceNDArray, # (n_samples,) int32 - input/output - split_feature: int32, # Scalar feature index - split_threshold: int32, # Scalar threshold -): - """Partition all samples using scalar split values (symmetric tree). - - Faster than GPU array version - no global memory lookup for split info. - New leaf_id = 2 * old_leaf_id + (1 if goes_right else 0) - """ - sample_idx = cuda.grid(1) - n_samples = sample_leaf_ids.shape[0] - - if sample_idx >= n_samples: - return - - current_leaf = sample_leaf_ids[sample_idx] - bin_value = int32(binned[split_feature, sample_idx]) - goes_right = 1 if bin_value > split_threshold else 0 - sample_leaf_ids[sample_idx] = 2 * current_leaf + goes_right - - -@cuda.jit -def _compute_leaf_ids_symmetric_kernel( - binned: DeviceNDArray, # (n_features, n_samples) uint8 - sample_leaf_ids: DeviceNDArray, # (n_samples,) int32 - output - level_features: DeviceNDArray, # (max_depth,) int32 - level_thresholds: DeviceNDArray, # (max_depth,) int32 - actual_depth: int32, -): - """Compute leaf IDs for ALL samples in ONE pass - CatBoost's key optimization. - - Instead of partitioning 6 times, we compute the final leaf directly: - leaf_idx = sum over d: 2^(depth-1-d) * (X[features[d]] > thresholds[d]) - - This is O(N * depth) work in ONE kernel vs O(N * depth) work in depth kernels. - The speedup comes from reduced kernel launch overhead and better cache usage. - """ - sample_idx = cuda.grid(1) - n_samples = sample_leaf_ids.shape[0] - - if sample_idx >= n_samples: - return - - leaf_idx = int32(0) - - for d in range(actual_depth): - feature = level_features[d] - if feature < 0: - break - threshold = level_thresholds[d] - bin_value = int32(binned[feature, sample_idx]) - goes_right = 1 if bin_value > threshold else 0 - leaf_idx = 2 * leaf_idx + goes_right - - sample_leaf_ids[sample_idx] = leaf_idx - - -@cuda.jit -def _compute_symmetric_leaf_values_kernel( - grad: DeviceNDArray, # (n_samples,) float32 - hess: DeviceNDArray, # (n_samples,) float32 - sample_leaf_ids: DeviceNDArray, # (n_samples,) int32 - n_leaves: int32, - reg_lambda: float32, - leaf_sum_grad: DeviceNDArray, # (n_leaves,) float32 - output - leaf_sum_hess: DeviceNDArray, # (n_leaves,) float32 - output -): - """Accumulate grad/hess per leaf for symmetric tree.""" - sample_idx = cuda.grid(1) - n_samples = grad.shape[0] - - if sample_idx >= n_samples: - return - - leaf_idx = sample_leaf_ids[sample_idx] - if leaf_idx < n_leaves: - cuda.atomic.add(leaf_sum_grad, leaf_idx, grad[sample_idx]) - cuda.atomic.add(leaf_sum_hess, leaf_idx, hess[sample_idx]) - - -@cuda.jit -def _init_symmetric_kernel(leaf_ids, leaf_g, leaf_h, n_samples, n_leaves): - """Initialize symmetric tree arrays. Module-level to avoid JIT overhead.""" - idx = cuda.grid(1) - if idx < n_samples: - leaf_ids[idx] = 0 - if idx < n_leaves: - leaf_g[idx] = float32(0.0) - leaf_h[idx] = float32(0.0) - - -@cuda.jit -def _finalize_leaf_values_kernel(leaf_vals, sum_grad, sum_hess, reg_lambda, n): - """Compute final leaf values. Module-level to avoid JIT overhead.""" - idx = cuda.grid(1) - if idx < n: - h = sum_hess[idx] - if h + reg_lambda > float32(0.0): - leaf_vals[idx] = float32(-sum_grad[idx] / (h + reg_lambda)) - else: - leaf_vals[idx] = float32(0.0) - - -def build_tree_symmetric_gpu_native( - binned: DeviceNDArray, - grad: DeviceNDArray, - hess: DeviceNDArray, - max_depth: int = 6, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - min_gain: float = 0.0, -) -> tuple[DeviceNDArray, DeviceNDArray, DeviceNDArray]: - """Build symmetric tree entirely on GPU. - - Phase 3.4: Oblivious trees with ONE split per level. - - KNOWN DEFECT (CRIT-5): produces degenerate trees. The histogram kernel - below ignores per-leaf sample assignments, so each depth sees the same - global histogram and repeats the same split. Callers - (``fit_tree_symmetric_gpu_native``) fall back to the correct CPU builder - unless OPENBOOST_EXPERIMENTAL_SYMMETRIC_GPU=1 is set. - - Args: - binned: Binned feature matrix, shape (n_features, n_samples), uint8 - grad: Gradient vector, shape (n_samples,), float32 - hess: Hessian vector, shape (n_samples,), float32 - max_depth: Maximum tree depth - reg_lambda: L2 regularization - min_child_weight: Minimum hessian sum in child - min_gain: Minimum gain to split - - Returns: - level_features: (max_depth,) int32 - feature at each level - level_thresholds: (max_depth,) int32 - threshold at each level - leaf_values: (2^max_depth,) float32 - leaf values - """ - n_features, n_samples = binned.shape - n_leaves = 2 ** max_depth - - threads = 256 - sample_blocks = math.ceil(n_samples / threads) - leaf_blocks = math.ceil(n_leaves / threads) - - # Allocate GPU arrays - sample_leaf_ids = cuda.device_array(n_samples, dtype=np.int32) - hist_grad = cuda.device_array((n_features, 256), dtype=np.float32) - hist_hess = cuda.device_array((n_features, 256), dtype=np.float32) - - leaf_sum_grad = cuda.device_array(n_leaves, dtype=np.float32) - leaf_sum_hess = cuda.device_array(n_leaves, dtype=np.float32) - - # Per-feature split finding outputs - per_feature_gains = cuda.device_array(n_features, dtype=np.float32) - per_feature_thresholds = cuda.device_array(n_features, dtype=np.int32) - - # Convert params to float32 - reg_lambda_f32 = np.float32(reg_lambda) - min_child_weight_f32 = np.float32(min_child_weight) - np.float32(min_gain) - - # Initialize GPU arrays (using module-level kernel to avoid JIT overhead) - init_blocks = max(sample_blocks, leaf_blocks, 1) - _init_symmetric_kernel[init_blocks, threads]( - sample_leaf_ids, leaf_sum_grad, leaf_sum_hess, n_samples, n_leaves - ) - - # CPU arrays for level splits - level_features_cpu = np.full(max_depth, -1, dtype=np.int32) - level_thresholds_cpu = np.zeros(max_depth, dtype=np.int32) - actual_depth = 0 - - # GPU arrays for incremental partitioning (needed to rebuild histograms) - level_features_gpu = cuda.to_device(level_features_cpu) - level_thresholds_gpu = cuda.to_device(level_thresholds_cpu) - - # BROKEN (CRIT-5): _build_symmetric_histogram_kernel does not take - # sample_leaf_ids, so this loop rebuilds the IDENTICAL global histogram at - # every depth and picks the same split again. Aggregating one histogram - # (or summing histograms) over all samples is NOT equivalent to summing - # per-leaf split gains — the pooled histogram is invariant to the leaf - # partition, so the level-2+ splits carry no new information. The fix is - # to accumulate per-leaf histograms (indexed by sample_leaf_ids) and sum - # each candidate's gain across leaves, mirroring the CPU - # SymmetricGrowth._find_level_split. This path is gated behind - # OPENBOOST_EXPERIMENTAL_SYMMETRIC_GPU=1 until that fix lands. - for depth in range(max_depth): - _build_symmetric_histogram_kernel[n_features, 256]( - binned, grad, hess, hist_grad, hist_hess - ) - - # Get totals for this depth - hist_grad_cpu = hist_grad.copy_to_host() - hist_hess_cpu = hist_hess.copy_to_host() - total_grad = np.float32(np.sum(hist_grad_cpu)) - total_hess = np.float32(np.sum(hist_hess_cpu)) - - # Find best split per feature on GPU (parallel prefix sum) - _find_symmetric_split_kernel[n_features, 256]( - hist_grad, hist_hess, total_grad, total_hess, - reg_lambda_f32, min_child_weight_f32, - per_feature_gains, per_feature_thresholds - ) - - # Single copy: get all feature gains/thresholds at once - gains_cpu = per_feature_gains.copy_to_host() - thresholds_cpu = per_feature_thresholds.copy_to_host() - - # CPU argmax (fast, only n_features elements) - best_feature = int(np.argmax(gains_cpu)) - best_gain = float(gains_cpu[best_feature]) - best_threshold = int(thresholds_cpu[best_feature]) - - if best_gain <= min_gain or best_threshold < 0: - break - - # Store split - level_features_cpu[depth] = best_feature - level_thresholds_cpu[depth] = best_threshold - actual_depth = depth + 1 - - # Partition samples by this depth's split so the next depth - # sees the updated leaf assignments. - _partition_symmetric_scalar_kernel[sample_blocks, threads]( - binned, sample_leaf_ids, - np.int32(best_feature), np.int32(best_threshold), - ) - - # Transfer final level arrays to GPU - level_features_gpu = cuda.to_device(level_features_cpu) - level_thresholds_gpu = cuda.to_device(level_thresholds_cpu) - - # Recompute final leaf IDs from scratch in one pass (canonical ordering) - _compute_leaf_ids_symmetric_kernel[sample_blocks, threads]( - binned, sample_leaf_ids, level_features_gpu, level_thresholds_gpu, actual_depth - ) - - # Compute leaf values - _compute_symmetric_leaf_values_kernel[sample_blocks, threads]( - grad, hess, sample_leaf_ids, n_leaves, reg_lambda_f32, - leaf_sum_grad, leaf_sum_hess - ) - - # Finalize leaf values on GPU (using module-level kernel) - leaf_values = cuda.device_array(n_leaves, dtype=np.float32) - - _finalize_leaf_values_kernel[leaf_blocks, threads]( - leaf_values, leaf_sum_grad, leaf_sum_hess, reg_lambda_f32, n_leaves - ) - - # Already on GPU - no transfer needed! - return level_features_gpu, level_thresholds_gpu, leaf_values - - -# ============================================================================= -# Multi-parameter objective kernels (unified trainer, K>1) -# ============================================================================= - -def _blocks_threads(n: int, threads: int = 256) -> tuple[int, int]: - return (n + threads - 1) // threads, threads - - -@cuda.jit -def _normal_ordinary_kernel(raw_loc, raw_scale, y, g_loc, h_loc, g_scale, h_scale, n): - """Ordinary NLL grad/hess for Normal: loc identity, scale exp-link.""" - i = cuda.grid(1) - if i >= n: - return - mu = raw_loc[i] - s = raw_scale[i] - if s > 20.0: - s = 20.0 - elif s < -20.0: - s = -20.0 - sigma = math.exp(s) - var = sigma * sigma - if var < 1e-12: - var = 1e-12 - resid = y[i] - mu - g_loc[i] = -resid / var - h_loc[i] = 1.0 / var - g_scale[i] = 1.0 - (resid * resid) / var - h_scale[i] = 2.0 - - -@cuda.jit -def _normal_natural_kernel(raw_loc, raw_scale, y, g_loc, h_loc, g_scale, h_scale, n): - """Natural gradient for Normal (diagonal Fisher): F=diag(1/σ², 2).""" - i = cuda.grid(1) - if i >= n: - return - mu = raw_loc[i] - s = raw_scale[i] - if s > 20.0: - s = 20.0 - elif s < -20.0: - s = -20.0 - sigma = math.exp(s) - var = sigma * sigma - if var < 1e-12: - var = 1e-12 - resid = y[i] - mu - g_loc[i] = -resid - h_loc[i] = 1.0 - g_scale[i] = 0.5 * (1.0 - (resid * resid) / var) - h_scale[i] = 1.0 - - -@cuda.jit -def _poisson_ordinary_kernel(raw_rate, y, g, h, n): - """Ordinary NLL grad/hess for Poisson with exp link: λ=exp(raw).""" - i = cuda.grid(1) - if i >= n: - return - r = raw_rate[i] - if r > 20.0: - r = 20.0 - elif r < -20.0: - r = -20.0 - lam = math.exp(r) - g[i] = lam - y[i] - h[i] = lam if lam > 1e-6 else 1e-6 - - -@cuda.jit -def _poisson_natural_kernel(raw_rate, y, g, h, n): - """Natural gradient for Poisson: F=λ, nat=(λ-y)/λ, unit hessian.""" - i = cuda.grid(1) - if i >= n: - return - r = raw_rate[i] - if r > 20.0: - r = 20.0 - elif r < -20.0: - r = -20.0 - lam = math.exp(r) - if lam < 1e-12: - lam = 1e-12 - g[i] = 1.0 - y[i] / lam - h[i] = 1.0 - - -@cuda.jit -def _scale_gh_kernel(grad, hess, weight, n): - i = cuda.grid(1) - if i < n: - w = weight[i] - grad[i] *= w - hess[i] *= w - - -def normal_step_gpu(raw_loc, raw_scale, y, *, natural: bool): - """In-place Normal (grad, hess) on device. Returns four device arrays.""" - n = int(y.shape[0]) - g_loc = cuda.device_array(n, dtype=np.float32) - h_loc = cuda.device_array(n, dtype=np.float32) - g_scale = cuda.device_array(n, dtype=np.float32) - h_scale = cuda.device_array(n, dtype=np.float32) - blocks, threads = _blocks_threads(n) - kern = _normal_natural_kernel if natural else _normal_ordinary_kernel - kern[blocks, threads](raw_loc, raw_scale, y, g_loc, h_loc, g_scale, h_scale, n) - return g_loc, h_loc, g_scale, h_scale - - -def poisson_step_gpu(raw_rate, y, *, natural: bool): - """In-place Poisson (grad, hess) on device.""" - n = int(y.shape[0]) - g = cuda.device_array(n, dtype=np.float32) - h = cuda.device_array(n, dtype=np.float32) - blocks, threads = _blocks_threads(n) - kern = _poisson_natural_kernel if natural else _poisson_ordinary_kernel - kern[blocks, threads](raw_rate, y, g, h, n) - return g, h - - -def scale_gh_gpu(grad, hess, weight) -> None: - """Multiply grad and hess by per-sample weights, in place.""" - n = int(grad.shape[0]) - blocks, threads = _blocks_threads(n) - _scale_gh_kernel[blocks, threads](grad, hess, weight, n) diff --git a/src/openboost/_batch.py b/src/openboost/_batch.py deleted file mode 100644 index bb9139f..0000000 --- a/src/openboost/_batch.py +++ /dev/null @@ -1,280 +0,0 @@ -"""Configuration and state objects for training multiple models. - -The CPU implementation is the correctness reference for future fused GPU -kernels. It shares binned input data while keeping predictions and trees -separate for each hyperparameter configuration. -""" - -from __future__ import annotations - -from collections.abc import Sequence -from dataclasses import dataclass, field -from typing import TYPE_CHECKING - -import numpy as np - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -@dataclass -class ConfigBatch: - """Batch of hyperparameter configurations for train-many fitting. - - All arrays must have the same length (n_configs). - - Attributes: - max_depths: Maximum tree depth for each config - reg_lambdas: L2 regularization for each config - min_child_weights: Minimum hessian sum in leaves for each config - learning_rates: Learning rate (eta) for each config - n_rounds: Number of boosting rounds (same for all configs) - - Example: - >>> configs = ConfigBatch.from_grid( - ... max_depth=[4, 6, 8], - ... reg_lambda=[0.1, 1.0, 10.0], - ... learning_rate=[0.05, 0.1, 0.3], - ... ) - >>> print(f"Training {configs.n_configs} configurations") - """ - max_depths: NDArray # (n_configs,) int32 - reg_lambdas: NDArray # (n_configs,) float32 - min_child_weights: NDArray # (n_configs,) float32 - learning_rates: NDArray # (n_configs,) float32 - n_rounds: int = 100 - - # Internal state for batch training - _device_arrays: dict = field(default_factory=dict, repr=False) - - def __post_init__(self): - """Validate and convert arrays to correct dtypes.""" - self.max_depths = np.asarray(self.max_depths, dtype=np.int32) - self.reg_lambdas = np.asarray(self.reg_lambdas, dtype=np.float32) - self.min_child_weights = np.asarray(self.min_child_weights, dtype=np.float32) - self.learning_rates = np.asarray(self.learning_rates, dtype=np.float32) - - if self.n_rounds < 1: - raise ValueError("n_rounds must be at least 1") - - # Validate shapes - n = len(self.max_depths) - if n == 0: - raise ValueError("At least one configuration is required") - if not all(len(arr) == n for arr in [ - self.reg_lambdas, self.min_child_weights, self.learning_rates - ]): - raise ValueError("All config arrays must have the same length") - - @property - def n_configs(self) -> int: - """Number of configurations in this batch.""" - return len(self.max_depths) - - @classmethod - def from_grid( - cls, - max_depth: Sequence[int] = (6,), - reg_lambda: Sequence[float] = (1.0,), - min_child_weight: Sequence[float] = (1.0,), - learning_rate: Sequence[float] = (0.1,), - n_rounds: int = 100, - ) -> ConfigBatch: - """Create a ConfigBatch from a grid of hyperparameters. - - Generates all combinations of the provided hyperparameters. - - Args: - max_depth: List of max_depth values - reg_lambda: List of reg_lambda values - min_child_weight: List of min_child_weight values - learning_rate: List of learning_rate values - n_rounds: Number of boosting rounds - - Returns: - ConfigBatch with all combinations - - Example: - >>> configs = ConfigBatch.from_grid( - ... max_depth=[4, 6], - ... reg_lambda=[0.1, 1.0], - ... ) - >>> configs.n_configs # 2 × 2 = 4 combinations - 4 - """ - import itertools - - # Generate all combinations - combinations = list(itertools.product( - max_depth, reg_lambda, min_child_weight, learning_rate - )) - - if not combinations: - raise ValueError("At least one value required for each hyperparameter") - - max_depths = np.array([c[0] for c in combinations], dtype=np.int32) - reg_lambdas = np.array([c[1] for c in combinations], dtype=np.float32) - min_child_weights = np.array([c[2] for c in combinations], dtype=np.float32) - learning_rates = np.array([c[3] for c in combinations], dtype=np.float32) - - return cls( - max_depths=max_depths, - reg_lambdas=reg_lambdas, - min_child_weights=min_child_weights, - learning_rates=learning_rates, - n_rounds=n_rounds, - ) - - @classmethod - def from_lists( - cls, - max_depths: Sequence[int], - reg_lambdas: Sequence[float], - min_child_weights: Sequence[float], - learning_rates: Sequence[float], - n_rounds: int = 100, - ) -> ConfigBatch: - """Create a ConfigBatch from explicit lists of hyperparameters. - - Each list must have the same length. Config i uses the i-th element - from each list. - - Args: - max_depths: List of max_depth values - reg_lambdas: List of reg_lambda values - min_child_weights: List of min_child_weight values - learning_rates: List of learning_rate values - n_rounds: Number of boosting rounds - - Returns: - ConfigBatch with specified configurations - """ - return cls( - max_depths=np.array(max_depths, dtype=np.int32), - reg_lambdas=np.array(reg_lambdas, dtype=np.float32), - min_child_weights=np.array(min_child_weights, dtype=np.float32), - learning_rates=np.array(learning_rates, dtype=np.float32), - n_rounds=n_rounds, - ) - - def to_device(self): - """Transfer config arrays to GPU. - - Call this once before batch training to avoid repeated transfers. - """ - from ._backends import is_cuda - - if not is_cuda(): - return - - if self._device_arrays: - return # Already on device - - from numba import cuda - - self._device_arrays = { - 'max_depths': cuda.to_device(self.max_depths), - 'reg_lambdas': cuda.to_device(self.reg_lambdas), - 'min_child_weights': cuda.to_device(self.min_child_weights), - 'learning_rates': cuda.to_device(self.learning_rates), - } - - def get_device_arrays(self) -> dict: - """Get GPU arrays for this config batch. - - Automatically transfers to device if not already done. - - Returns: - Dict with 'max_depths', 'reg_lambdas', 'min_child_weights', 'learning_rates' - """ - if not self._device_arrays: - self.to_device() - return self._device_arrays - - def __getitem__(self, idx: int) -> dict: - """Get a single configuration as a dict. - - Args: - idx: Configuration index - - Returns: - Dict with hyperparameters for config idx - """ - return { - 'max_depth': int(self.max_depths[idx]), - 'reg_lambda': float(self.reg_lambdas[idx]), - 'min_child_weight': float(self.min_child_weights[idx]), - 'learning_rate': float(self.learning_rates[idx]), - 'n_rounds': self.n_rounds, - } - - def __iter__(self): - """Iterate over configurations.""" - for i in range(self.n_configs): - yield self[i] - - def __repr__(self) -> str: - return ( - f"ConfigBatch(n_configs={self.n_configs}, n_rounds={self.n_rounds}, " - f"max_depths={self.max_depths.tolist()}, " - f"reg_lambdas={self.reg_lambdas.tolist()}, ...)" - ) - - -@dataclass -class BatchTrainingState: - """Independent prediction and tree state for multiple models. - - Tracks per-config predictions and trees during training. - - Attributes: - n_configs: Number of configurations - n_samples: Number of training samples - predictions: Current predictions, shape (n_configs, n_samples) - trees: List of tree lists, one per config - """ - n_configs: int - n_samples: int - predictions: NDArray # (n_configs, n_samples) float32 - trees: list = field(default_factory=list) # List[List[Tree]] - - @classmethod - def create(cls, n_configs: int, n_samples: int) -> BatchTrainingState: - """Create initial training state. - - Args: - n_configs: Number of configurations - n_samples: Number of training samples - - Returns: - Initialized BatchTrainingState with zero predictions - """ - predictions = np.zeros((n_configs, n_samples), dtype=np.float32) - trees = [[] for _ in range(n_configs)] - return cls( - n_configs=n_configs, - n_samples=n_samples, - predictions=predictions, - trees=trees, - ) - - def to_device(self): - """Transfer predictions to GPU.""" - from ._backends import is_cuda - - if is_cuda() and not hasattr(self.predictions, '__cuda_array_interface__'): - from numba import cuda - self.predictions = cuda.to_device(self.predictions) - - def get_predictions(self, config_idx: int) -> NDArray: - """Get predictions for a specific config. - - Args: - config_idx: Configuration index - - Returns: - Predictions array, shape (n_samples,) - """ - if hasattr(self.predictions, 'copy_to_host'): - return self.predictions[config_idx].copy_to_host() - return self.predictions[config_idx] diff --git a/src/openboost/_callbacks.py b/src/openboost/_callbacks.py deleted file mode 100644 index 83db36f..0000000 --- a/src/openboost/_callbacks.py +++ /dev/null @@ -1,443 +0,0 @@ -"""Callback system for OpenBoost training. - -Phase 13: Pluggable hooks for training (early stopping, logging, checkpoints). - -Inspired by Keras/PyTorch Lightning - allows customizable behavior without -modifying core training loops. Works with any model (GradientBoosting, DART, -OpenBoostGAM, etc.). - -Example: - >>> from openboost import GradientBoosting, EarlyStopping, Logger - >>> - >>> model = GradientBoosting(n_trees=1000) - >>> model.fit(X, y, - ... callbacks=[ - ... EarlyStopping(patience=50), - ... Logger(period=10), - ... ], - ... eval_set=[(X_val, y_val)] - ... ) - >>> print(f"Stopped at iteration {model.best_iteration_}") -""" - -from __future__ import annotations - -import copy -import warnings -from abc import ABC -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - pass - - -@dataclass -class TrainingState: - """Shared state passed to callbacks during training. - - This object is passed to all callbacks at each training event, - allowing them to inspect and modify training behavior. - - Attributes: - model: The model being trained (modified in place). - round_idx: Current boosting round (0-indexed). - n_rounds: Total number of rounds requested. - train_loss: Training loss for current round (if computed). - val_loss: Validation loss for current round (if eval_set provided). - extra: Dict for custom data (research callbacks can use this). - """ - model: Any - round_idx: int = 0 - n_rounds: int = 0 - train_loss: float | None = None - val_loss: float | None = None - extra: dict = field(default_factory=dict) - - -class Callback(ABC): # noqa: B024 - """Base class for training callbacks. - - Subclass this to create custom callbacks for training hooks. - All methods are optional - override only what you need. - - Example (custom callback): - >>> class GradientTracker(Callback): - ... def __init__(self): - ... self.grad_norms = [] - ... - ... def on_round_end(self, state): - ... if 'grad_norm' in state.extra: - ... self.grad_norms.append(state.extra['grad_norm']) - ... return True - >>> - >>> tracker = GradientTracker() - >>> model.fit(X, y, callbacks=[tracker]) - >>> plt.plot(tracker.grad_norms) - """ - - def on_train_begin(self, state: TrainingState) -> None: # noqa: B027 - """Called at the start of training. - - Args: - state: Current training state. - """ - pass - - def on_round_begin(self, state: TrainingState) -> None: # noqa: B027 - """Called at the start of each boosting round. - - Args: - state: Current training state. - """ - pass - - def on_round_end(self, state: TrainingState) -> bool: - """Called at the end of each boosting round. - - Args: - state: Current training state. - - Returns: - True to continue training, False to stop early. - """ - return True - - def on_train_end(self, state: TrainingState) -> None: # noqa: B027 - """Called at the end of training. - - Args: - state: Current training state. - """ - pass - - -class EarlyStopping(Callback): - """Stop training when validation metric stops improving. - - Works with ANY model that provides val_loss in TrainingState. - Requires `eval_set` to be passed to `fit()`. - - Args: - patience: Number of rounds without improvement before stopping. - min_delta: Minimum change to qualify as an improvement. - restore_best: If True, restore model to best iteration after stopping. - verbose: If True, print message when stopping. - - Attributes (after training): - best_score: Best validation score achieved. - best_round: Round at which best score was achieved. - stopped_round: Round at which training was stopped (or None). - - Example: - >>> callback = EarlyStopping(patience=50, min_delta=1e-4) - >>> model.fit(X, y, callbacks=[callback], eval_set=[(X_val, y_val)]) - >>> print(f"Best round: {model.best_iteration_}") - """ - - def __init__( - self, - patience: int = 50, - min_delta: float = 0.0, - restore_best: bool = True, - verbose: bool = False, - ): - self.patience = patience - self.min_delta = min_delta - self.restore_best = restore_best - self.verbose = verbose - - # State - self.best_score: float = float('inf') - self.best_round: int = 0 - self.wait: int = 0 - self.stopped_round: int | None = None - self._best_trees: list | None = None - self._best_weights: list | None = None # For DART - self._best_tree_weights: list | None = None - self._best_base_score: float | None = None - - def on_train_begin(self, state: TrainingState) -> None: - """Reset state at start of training.""" - self.best_score = float('inf') - self.best_round = 0 - self.wait = 0 - self.stopped_round = None - self._best_trees = None - self._best_weights = None - self._best_tree_weights = None - self._best_base_score = None - - def on_round_end(self, state: TrainingState) -> bool: - """Check if we should stop training.""" - if state.val_loss is None: - return True # No validation set, continue - - current_score = state.val_loss - - if current_score < self.best_score - self.min_delta: - # Improvement found - self.best_score = current_score - self.best_round = state.round_idx - self.wait = 0 - - if self.restore_best: - # Snapshot current model state - self._best_trees = copy.deepcopy(state.model.trees_) - # Handle DART tree weights - self._best_tree_weights = copy.deepcopy(getattr(state.model, 'tree_weights_', None)) - self._best_base_score = copy.deepcopy(getattr(state.model, 'base_score_', None)) - else: - self.wait += 1 - if self.wait >= self.patience: - self.stopped_round = state.round_idx - if self.verbose: - print(f"Early stopping at round {state.round_idx}. " - f"Best was round {self.best_round} with score {self.best_score:.6f}") - return False # Stop training - - return True - - def on_train_end(self, state: TrainingState) -> None: - """Restore best model if requested.""" - if self.restore_best and self._best_trees is not None: - state.model.trees_ = self._best_trees - if self._best_tree_weights is not None and hasattr(state.model, 'tree_weights_'): - state.model.tree_weights_ = self._best_tree_weights - if self._best_base_score is not None and hasattr(state.model, 'base_score_'): - state.model.base_score_ = self._best_base_score - - # Set attributes on model - state.model.best_iteration_ = self.best_round - state.model.best_score_ = self.best_score - - -class Logger(Callback): - """Log training progress to stdout. - - Args: - period: Print every N rounds (default: 1). - show_train: Include training loss in output. - show_val: Include validation loss in output. - - Example: - >>> callback = Logger(period=10) # Log every 10 rounds - >>> model.fit(X, y, callbacks=[callback], eval_set=[(X_val, y_val)]) - [0] train: 0.5234 valid: 0.5456 - [10] train: 0.2134 valid: 0.2345 - [20] train: 0.1234 valid: 0.1456 - ... - """ - - def __init__( - self, - period: int = 1, - show_train: bool = True, - show_val: bool = True, - ): - self.period = max(1, period) - self.show_train = show_train - self.show_val = show_val - - def on_round_end(self, state: TrainingState) -> bool: - """Print progress if at logging period.""" - if state.round_idx % self.period == 0: - parts = [f"[{state.round_idx}]"] - - if self.show_train and state.train_loss is not None: - parts.append(f"train: {state.train_loss:.6f}") - - if self.show_val and state.val_loss is not None: - parts.append(f"valid: {state.val_loss:.6f}") - - if len(parts) > 1: # Have something to print - print(" ".join(parts)) - - return True - - -class ModelCheckpoint(Callback): - """Save model periodically or when validation score improves. - - Args: - filepath: Path to save model (use .pkl extension). - save_best_only: If True, only save when validation improves. - verbose: If True, print message when saving. - - Example: - >>> callback = ModelCheckpoint('best_model.pkl', save_best_only=True) - >>> model.fit(X, y, callbacks=[callback], eval_set=[(X_val, y_val)]) - """ - - def __init__( - self, - filepath: str, - save_best_only: bool = True, - verbose: bool = False, - ): - self.filepath = filepath - self.save_best_only = save_best_only - self.verbose = verbose - self.best_score: float = float('inf') - - def on_round_end(self, state: TrainingState) -> bool: - """Save model if conditions are met.""" - should_save = False - - if not self.save_best_only: - should_save = True - elif state.val_loss is not None and state.val_loss < self.best_score: - self.best_score = state.val_loss - should_save = True - - if should_save: - self._save_model(state.model) - if self.verbose: - print(f"Model saved to {self.filepath}") - - return True - - def _save_model(self, model) -> None: - """Save model using its save() method, falling back to pickle.""" - if hasattr(model, 'save'): - model.save(self.filepath) - else: - import pickle - import warnings - warnings.warn( - f"{type(model).__name__} does not have a save() method. " - "Falling back to pickle.dump.", - UserWarning, - stacklevel=2, - ) - with open(self.filepath, 'wb') as f: - pickle.dump(model, f) - - -class LearningRateScheduler(Callback): - """Adjust learning rate during training. - - Args: - schedule: Function (round_idx) -> learning_rate_multiplier - - Example: - >>> # Decay learning rate by 0.95 each round - >>> scheduler = LearningRateScheduler(lambda r: 0.95 ** r) - >>> model.fit(X, y, callbacks=[scheduler]) - - >>> # Step decay: halve LR at round 50 and 100 - >>> def step_decay(r): - ... if r < 50: return 1.0 - ... elif r < 100: return 0.5 - ... else: return 0.25 - >>> scheduler = LearningRateScheduler(step_decay) - """ - - def __init__(self, schedule): - self.schedule = schedule - self._initial_lr: float | None = None - - def on_train_begin(self, state: TrainingState) -> None: - """Store initial learning rate.""" - self._initial_lr = state.model.learning_rate - - def on_round_begin(self, state: TrainingState) -> None: - """Update learning rate for this round.""" - if self._initial_lr is not None: - multiplier = self.schedule(state.round_idx) - state.model.learning_rate = self._initial_lr * multiplier - - -class HistoryCallback(Callback): - """Record training history (losses per round). - - Attributes (after training): - history: Dict with 'train_loss' and 'val_loss' lists. - - Example: - >>> history = HistoryCallback() - >>> model.fit(X, y, callbacks=[history], eval_set=[(X_val, y_val)]) - >>> plt.plot(history.history['train_loss'], label='train') - >>> plt.plot(history.history['val_loss'], label='valid') - >>> plt.legend() - """ - - def __init__(self): - self.history: dict[str, list[float]] = { - 'train_loss': [], - 'val_loss': [], - } - - def on_train_begin(self, state: TrainingState) -> None: - """Reset history.""" - self.history = {'train_loss': [], 'val_loss': []} - - def on_round_end(self, state: TrainingState) -> bool: - """Record losses.""" - if state.train_loss is not None: - self.history['train_loss'].append(state.train_loss) - if state.val_loss is not None: - self.history['val_loss'].append(state.val_loss) - return True - - -class CallbackManager: - """Orchestrates multiple callbacks. - - Used internally by training loops to manage callback execution. - - Args: - callbacks: List of Callback instances. - """ - - def __init__(self, callbacks: list[Callback] | None = None): - self.callbacks = list(callbacks) if callbacks else [] - - def on_train_begin(self, state: TrainingState) -> None: - """Call on_train_begin for all callbacks.""" - for cb in self.callbacks: - cb.on_train_begin(state) - - def on_round_begin(self, state: TrainingState) -> None: - """Call on_round_begin for all callbacks.""" - for cb in self.callbacks: - cb.on_round_begin(state) - - def on_round_end(self, state: TrainingState) -> bool: - """Call on_round_end for all callbacks. - - Returns: - True if training should continue, False if any callback wants to stop. - """ - should_continue = True - for cb in self.callbacks: - if not cb.on_round_end(state): - should_continue = False - return should_continue - - def on_train_end(self, state: TrainingState) -> None: - """Call on_train_end for all callbacks.""" - for cb in self.callbacks: - cb.on_train_end(state) - - -def warn_if_early_stopping_without_eval_set( - callbacks: list[Callback] | None, - eval_set: object | None, -) -> None: - """Warn if an ``EarlyStopping`` callback is set but no ``eval_set`` is given. - - Early stopping needs a validation set to compute the monitored metric, so - without ``eval_set`` it silently has no effect. Shared by all model fit - paths to keep the message and behavior consistent. - """ - if eval_set is not None or not callbacks: - return - if any(isinstance(cb, EarlyStopping) for cb in callbacks): - warnings.warn( - "EarlyStopping callback provided but eval_set is None. " - "Early stopping requires eval_set to compute validation " - "loss and will have no effect.", - UserWarning, - stacklevel=3, - ) diff --git a/src/openboost/_core/__init__.py b/src/openboost/_core/__init__.py deleted file mode 100644 index 4faee93..0000000 --- a/src/openboost/_core/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Core tree building infrastructure. - -This module contains the foundation for tree building: -- Primitives: histogram building, split finding, partitioning -- Growth strategies: level-wise, leaf-wise, symmetric -- fit_tree: main entry point for building trees -""" - -from ._growth import ( - GrowthConfig, - GrowthStrategy, - LeafValues, - LeafWiseGrowth, - LevelWiseGrowth, - ScalarLeaves, - SymmetricGrowth, - TreeStructure, - VectorLeaves, - get_growth_strategy, -) -from ._histogram import build_histogram -from ._predict import predict_ensemble -from ._primitives import ( - NodeHistogram, - NodeSplit, - build_node_histograms, - compute_leaf_values, - find_node_splits, - get_children, - get_nodes_at_depth, - get_parent, - init_sample_node_ids, - partition_samples, - subtract_histogram, -) -from ._split import SplitInfo, compute_leaf_value, find_best_split -from ._tree import ( - SymmetricTree, - Tree, - TreeNode, - fit_tree, - fit_tree_gpu_native, - fit_tree_symmetric, - fit_tree_symmetric_gpu_native, - fit_trees_batch, - predict_symmetric_tree, - predict_tree, -) - -__all__ = [ - # Primitives - "NodeHistogram", - "NodeSplit", - "build_node_histograms", - "subtract_histogram", - "find_node_splits", - "partition_samples", - "compute_leaf_values", - "init_sample_node_ids", - "get_nodes_at_depth", - "get_children", - "get_parent", - # Growth - "GrowthConfig", - "GrowthStrategy", - "TreeStructure", - "LevelWiseGrowth", - "LeafWiseGrowth", - "SymmetricGrowth", - "get_growth_strategy", - "LeafValues", - "ScalarLeaves", - "VectorLeaves", - # Tree - "fit_tree", - "fit_trees_batch", - "Tree", - "TreeNode", - "SymmetricTree", - "fit_tree_symmetric", - "fit_tree_symmetric_gpu_native", - "fit_tree_gpu_native", - "predict_tree", - "predict_symmetric_tree", - # Low-level - "build_histogram", - "find_best_split", - "compute_leaf_value", - "SplitInfo", - "predict_ensemble", -] diff --git a/src/openboost/_core/_growth.py b/src/openboost/_core/_growth.py deleted file mode 100644 index 881efdb..0000000 --- a/src/openboost/_core/_growth.py +++ /dev/null @@ -1,1326 +0,0 @@ -"""Tree growth strategies for OpenBoost. - -Phase 8.2: Abstraction for different tree growth approaches. -Phase 9.0: Decoupled leaf values for flexibility (multi-output, distributions, etc.) -Phase 14: Added missing value handling - learns optimal direction for NaN values. - -Each strategy uses the primitives from `_primitives.py` but orchestrates -them differently: - -- LevelWiseGrowth: Process all nodes at each depth (XGBoost default) -- LeafWiseGrowth: Always split the best leaf (LightGBM style) -- SymmetricGrowth: Same split at each depth (CatBoost style) -""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Protocol, runtime_checkable - -import numpy as np - -from .._array import MISSING_BIN -from .._backends import is_cuda -from ._primitives import ( - NodeHistogram, - NodeSplit, - build_node_histograms, - compute_leaf_values, - find_node_splits, - get_nodes_at_depth, - init_sample_node_ids, - partition_samples, - subtract_histogram, -) - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -# ============================================================================= -# Leaf Values Abstraction (Phase 9.0) -# ============================================================================= - -@runtime_checkable -class LeafValues(Protocol): - """Protocol for leaf value storage. - - This abstraction allows trees to store different types of values: - - ScalarLeaves: Standard float per leaf (default) - - VectorLeaves: Multiple floats per leaf (multi-output) - - DistributionLeaves: Distribution parameters (uncertainty) - """ - - def __getitem__(self, indices: NDArray) -> NDArray: - """Get values for given leaf indices.""" - ... - - def __setitem__(self, index: int, value) -> None: - """Set value for a leaf.""" - ... - - @property - def shape(self) -> tuple: - """Shape of the storage array.""" - ... - - -@dataclass -class ScalarLeaves: - """Standard scalar leaf values (default). - - Each leaf stores a single float value. - Shape: (n_nodes,) - """ - _values: NDArray # (n_nodes,) float32 - - def __getitem__(self, indices: NDArray) -> NDArray: - """Get scalar values for indices.""" - return self._values[indices] - - def __setitem__(self, index: int, value: float) -> None: - """Set scalar value.""" - self._values[index] = value - - @property - def shape(self) -> tuple: - return self._values.shape - - @property - def values(self) -> NDArray: - """Direct access to underlying array (for backward compatibility).""" - return self._values - - @classmethod - def zeros(cls, n_nodes: int) -> ScalarLeaves: - """Create zero-initialized scalar leaves.""" - return cls(_values=np.zeros(n_nodes, dtype=np.float32)) - - -@dataclass -class VectorLeaves: - """Multi-output leaf values. - - Each leaf stores a vector of values. - Shape: (n_nodes, n_outputs) - """ - _values: NDArray # (n_nodes, n_outputs) float32 - n_outputs: int - - def __getitem__(self, indices: NDArray) -> NDArray: - """Get vector values for indices. Returns (n_indices, n_outputs).""" - return self._values[indices] - - def __setitem__(self, index: int, value: NDArray) -> None: - """Set vector value.""" - self._values[index] = value - - @property - def shape(self) -> tuple: - return self._values.shape - - @property - def values(self) -> NDArray: - """Direct access to underlying array.""" - return self._values - - @classmethod - def zeros(cls, n_nodes: int, n_outputs: int) -> VectorLeaves: - """Create zero-initialized vector leaves.""" - return cls( - _values=np.zeros((n_nodes, n_outputs), dtype=np.float32), - n_outputs=n_outputs - ) - - -# ============================================================================= -# Configuration -# ============================================================================= - -@dataclass -class GrowthConfig: - """Configuration for tree growth. - - Args: - max_depth: Maximum tree depth (for level-wise and symmetric) - max_leaves: Maximum number of leaves (for leaf-wise) - min_child_weight: Minimum sum of hessian in each child - reg_lambda: L2 regularization on leaf values - reg_alpha: L1 regularization on leaf values (Phase 11) - min_gain: Minimum gain required to split (alias: gamma) - subsample: Row sampling ratio per tree (Phase 11) - colsample_bytree: Column sampling ratio per tree (Phase 11) - """ - max_depth: int = 6 - max_leaves: int | None = None # For leaf-wise growth - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 # Phase 11: L1 regularization - min_gain: float = 0.0 - subsample: float = 1.0 # Phase 11: row sampling - colsample_bytree: float = 1.0 # Phase 11: column sampling - - -# ============================================================================= -# Tree Structure (Struct-of-Arrays for GPU efficiency) -# ============================================================================= - -@dataclass -class TreeStructure: - """Struct-of-arrays tree representation. - - This is the output of all growth strategies. Can be used for prediction - on both CPU and GPU. - - For standard trees: - - Navigate using left/right children - - Leaf nodes have left_children[i] == -1 - - For symmetric trees: - - Use level_features/level_thresholds for navigation - - leaf_values has 2^depth entries - - Phase 9.0: Leaf values are now abstracted via LeafValues protocol. - This enables multi-output, distributions, embeddings, etc. - - Phase 14: Added missing_go_left for handling NaN values. - Phase 14.3: Added categorical split support. - """ - # Tree structure (routing) - features: NDArray # (n_nodes,) int32 - split feature (-1 for leaf) - thresholds: NDArray # (n_nodes,) int32 - split threshold (ordinal) or split_point (cat) - left_children: NDArray # (n_nodes,) int32 - left child (-1 for leaf) - right_children: NDArray # (n_nodes,) int32 - right child (-1 for leaf) - - # Leaf values (flexible - Phase 9.0) - # Can be NDArray for backward compat, or LeafValues subclass - values: NDArray | LeafValues # Default: (n_nodes,) float32 - - # Metadata - n_nodes: int - depth: int - n_features: int - - # For symmetric trees (optional) - is_symmetric: bool = False - level_features: NDArray | None = None # (depth,) int32 - level_thresholds: NDArray | None = None # (depth,) int32 - - # Phase 14: Missing value handling - missing_go_left: NDArray | None = None # (n_nodes,) bool - direction for NaN - - # Phase 14.3: Categorical split support - is_categorical_split: NDArray | None = None # (n_nodes,) bool - True if categorical split - cat_bitsets: NDArray | None = None # (n_nodes,) uint64 - bitmask for categories going left - - # Cached GPU arrays for fast repeated prediction (avoids re-transferring) - _gpu_arrays: dict | None = field(default=None, repr=False) - - def __getstate__(self) -> dict: - # Device handles cannot be deep-copied/pickled; host arrays are the - # source of truth, the cache re-uploads lazily (see Tree.__getstate__). - state = self.__dict__.copy() - state['_gpu_arrays'] = None - return state - - def __setstate__(self, state: dict) -> None: - self.__dict__.update(state) - - def get_leaf_values(self, leaf_ids: NDArray) -> NDArray: - """Get leaf values for given leaf IDs. - - This method abstracts over different leaf value storage types. - - Args: - leaf_ids: Array of leaf node indices - - Returns: - Values for those leaves (shape depends on leaf type) - """ - # Both plain ndarrays and LeafValues containers support indexing. - return self.values[leaf_ids] - - def set_leaf_value(self, leaf_id: int, value) -> None: - """Set a leaf value. - - Args: - leaf_id: Leaf node index - value: Value to store (scalar or array depending on leaf type) - """ - # Both plain ndarrays and LeafValues containers support item assignment. - self.values[leaf_id] = value - - @property - def leaf_values_array(self) -> NDArray: - """Get raw values array (for backward compatibility).""" - # ndarray check must come first: LeafValues is a runtime_checkable - # structural Protocol and a plain ndarray satisfies it, so the - # isinstance(..., LeafValues) branch would call ndarray.values. - if isinstance(self.values, np.ndarray): - return self.values - if isinstance(self.values, LeafValues): - return self.values.values - return self.values - - def predict(self, binned: NDArray) -> NDArray: - """Predict using this tree. - - Args: - binned: Binned features, shape (n_features, n_samples), uint8 - - Returns: - predictions: Shape (n_samples,), float32 - """ - if self.is_symmetric: - return self._predict_symmetric(binned) - return self._predict_standard(binned) - - def __call__(self, X) -> NDArray: - """Make tree callable for backward compatibility. - - Args: - X: BinnedArray or binned data array - - Returns: - predictions: Shape (n_samples,), float32 - """ - # Handle BinnedArray - from .._array import BinnedArray - binned = X.data if isinstance(X, BinnedArray) else X - return self.predict(binned) - - def _predict_standard(self, binned: NDArray) -> NDArray: - """Predict using standard tree traversal.""" - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - return self._predict_standard_gpu(binned) - return self._predict_standard_cpu(binned) - - def _predict_standard_cpu(self, binned: NDArray) -> NDArray: - """CPU prediction for standard trees. - - Phase 14: Handles missing values (bin 255) using learned direction. - Phase 14.3: Handles categorical splits using bitmask membership. - """ - binned = np.asarray(binned) - n_samples = binned.shape[1] - - # Check if we have missing value handling - has_missing_handling = self.missing_go_left is not None - # Phase 14.3: Check for categorical splits - has_categorical = self.is_categorical_split is not None and self.cat_bitsets is not None - - # Get leaf indices for all samples - leaf_ids = np.empty(n_samples, dtype=np.int32) - for i in range(n_samples): - node = 0 - while self.left_children[node] != -1: - feature = self.features[node] - threshold = self.thresholds[node] - bin_value = binned[feature, i] - - # Phase 14: Handle missing values first - if bin_value == MISSING_BIN and has_missing_handling: - # Use learned direction for missing values - if self.missing_go_left[node]: - node = self.left_children[node] - else: - node = self.right_children[node] - # Phase 14.3: Handle categorical splits - elif has_categorical and self.is_categorical_split[node]: - # Check bitmask membership: bit[bin_value] == 1 means go left. - # Bins >= 64 are not representable in the 64-bit bitset and - # always go right (consistent with CPU/GPU kernels). - bitset = self.cat_bitsets[node] - goes_left = ((bitset >> bin_value) & 1) if bin_value < 64 else 0 - node = self.left_children[node] if goes_left else self.right_children[node] - # Standard ordinal split - elif bin_value <= threshold: - node = self.left_children[node] - else: - node = self.right_children[node] - leaf_ids[i] = node - - # Get leaf values (works with both NDArray and LeafValues) - return self.get_leaf_values(leaf_ids) - - def _ensure_gpu_arrays(self): - """Cache tree structure arrays on GPU to avoid repeated transfers.""" - if self._gpu_arrays is not None: - return self._gpu_arrays - from .._backends._cuda import to_device - - self._gpu_arrays = { - 'features': to_device(self.features), - 'thresholds': to_device(self.thresholds.astype(np.uint8)), - 'values': to_device(self.values if isinstance(self.values, np.ndarray) else self.leaf_values_array), - 'left': to_device(self.left_children), - 'right': to_device(self.right_children), - 'missing_left': to_device(self.missing_go_left) if self.missing_go_left is not None else None, - } - has_categorical = ( - self.is_categorical_split is not None - and self.cat_bitsets is not None - and np.any(self.is_categorical_split) - ) - if has_categorical: - self._gpu_arrays['is_categorical'] = to_device(self.is_categorical_split) - self._gpu_arrays['cat_bitsets'] = to_device(self.cat_bitsets) - return self._gpu_arrays - - def _predict_standard_gpu(self, binned) -> NDArray: - """GPU prediction for standard trees.""" - from .._backends._cuda import predict_cuda, predict_with_categorical_cuda - - ga = self._ensure_gpu_arrays() - - if 'is_categorical' in ga: - return predict_with_categorical_cuda( - binned, - ga['features'], ga['thresholds'], ga['values'], - ga['left'], ga['right'], - tree_missing_left=ga['missing_left'], - is_categorical_split=ga['is_categorical'], - cat_bitsets=ga['cat_bitsets'], - ) - - return predict_cuda( - binned, - ga['features'], ga['thresholds'], ga['values'], - ga['left'], ga['right'], - tree_missing_left=ga['missing_left'], - ) - - def _predict_symmetric(self, binned: NDArray) -> NDArray: - """Predict using symmetric tree (bit operations).""" - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - return self._predict_symmetric_gpu(binned) - return self._predict_symmetric_cpu(binned) - - def _predict_symmetric_cpu(self, binned: NDArray) -> NDArray: - """CPU prediction for symmetric trees.""" - binned = np.asarray(binned) - n_samples = binned.shape[1] - leaf_ids = np.zeros(n_samples, dtype=np.int32) - - for d in range(self.depth): - if self.level_features[d] < 0: - break - feature = self.level_features[d] - threshold = self.level_thresholds[d] - goes_right = binned[feature, :] > threshold - leaf_ids = 2 * leaf_ids + goes_right.astype(np.int32) - - # Map leaf_ids (0 to 2^depth-1) to actual node indices - # Leaves start at index 2^depth - 1 in the tree array - leaf_start = 2**self.depth - 1 - return self.get_leaf_values(leaf_start + leaf_ids) - - def _predict_symmetric_gpu(self, binned) -> NDArray: - """GPU prediction for symmetric trees.""" - from .._backends._cuda import predict_symmetric_cuda - - # GPU kernel indexes leaves from 0, so pass only the leaf portion - leaf_start = 2**self.depth - 1 - leaf_values = self.values[leaf_start:leaf_start + 2**self.depth] - - return predict_symmetric_cuda( - binned, - self.level_features, - self.level_thresholds.astype(np.uint8), - leaf_values, - self.depth, - ) - - -# ============================================================================= -# Growth Strategy Base Class -# ============================================================================= - -class GrowthStrategy(ABC): - """Abstract base for tree growth strategies.""" - - @abstractmethod - def grow( - self, - binned: NDArray, - grad: NDArray, - hess: NDArray, - config: GrowthConfig, - has_missing: NDArray | None = None, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, - ) -> TreeStructure: - """Grow a tree using this strategy. - - Args: - binned: Binned features, shape (n_features, n_samples), uint8 - grad: Gradients, shape (n_samples,), float32 - hess: Hessians, shape (n_samples,), float32 - config: Growth configuration - has_missing: Boolean array (n_features,) indicating which features - have missing values (Phase 14). If None, no missing handling. - is_categorical: Boolean array (n_features,) indicating categorical features - (Phase 14.3). If None, all features are numeric. - n_categories: Array (n_features,) of category counts (0 for numeric) - - Returns: - TreeStructure ready for prediction - """ - ... - - -# ============================================================================= -# Level-Wise Growth (XGBoost default) -# ============================================================================= - -class LevelWiseGrowth(GrowthStrategy): - """Level-wise (depth-first) tree growth. - - Processes all nodes at each depth level before moving to the next. - This is the default strategy used by XGBoost. - - Characteristics: - - Balanced trees (all branches grow equally) - - O(depth) kernel launches on GPU - - Good GPU utilization (batch all nodes at a level) - - Phase 14: Added missing value handling. - Phase 14.3: Added categorical feature support. - """ - - def grow( - self, - binned: NDArray, - grad: NDArray, - hess: NDArray, - config: GrowthConfig, - has_missing: NDArray | None = None, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, - ) -> TreeStructure: - """Grow tree level by level. - - Phase 14: Added has_missing parameter for NaN handling. - Phase 14.3: Added categorical feature support. - """ - n_features, n_samples = binned.shape - max_nodes = 2**(config.max_depth + 1) - 1 - - # Initialize arrays - features = np.full(max_nodes, -1, dtype=np.int32) - thresholds = np.zeros(max_nodes, dtype=np.int32) - values = np.zeros(max_nodes, dtype=np.float32) - left_children = np.full(max_nodes, -1, dtype=np.int32) - right_children = np.full(max_nodes, -1, dtype=np.int32) - missing_go_left = np.ones(max_nodes, dtype=np.bool_) # Phase 14: default left - is_categorical_split = np.zeros(max_nodes, dtype=np.bool_) # Phase 14.3 - cat_bitsets = np.zeros(max_nodes, dtype=np.uint64) # Phase 14.3 - - # Track sample assignments - sample_node_ids = init_sample_node_ids(n_samples, device="cpu") - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - sample_node_ids = init_sample_node_ids(n_samples, device="cuda") - - # Track active nodes and their histograms for subtraction - parent_histograms: dict[int, NodeHistogram] = {} - - actual_depth = 0 - - # Column subsampling: select a random subset of features for this tree - if config.colsample_bytree < 1.0: - n_selected = max(1, int(n_features * config.colsample_bytree)) - selected_features = np.sort(np.random.choice(n_features, size=n_selected, replace=False)) - col_mask = np.zeros(n_features, dtype=np.bool_) - col_mask[selected_features] = True - else: - col_mask = None - - # Build level by level - for depth in range(config.max_depth): - nodes_at_level = get_nodes_at_depth(depth) - - # Filter to nodes that have samples - active_nodes = self._get_active_nodes(sample_node_ids, nodes_at_level) - if not active_nodes: - break - - # Histogram subtraction: build only smaller children, subtract for larger - if depth > 0 and parent_histograms: - build_nodes, subtract_info = self._plan_histogram_subtraction( - active_nodes, sample_node_ids, parent_histograms - ) - # Build histograms only for the subset that needs full computation - histograms = build_node_histograms( - binned, grad, hess, sample_node_ids, build_nodes - ) if build_nodes else {} - # Derive larger children's histograms via subtraction (O(features*bins)) - for child_id, (parent_hist, sibling_id) in subtract_info.items(): - histograms[child_id] = subtract_histogram( - parent_hist, histograms[sibling_id], child_id - ) - else: - histograms = build_node_histograms( - binned, grad, hess, sample_node_ids, active_nodes - ) - - # Column subsampling: zero out non-selected feature histograms - if col_mask is not None: - for _node_id, hist in histograms.items(): - hist.hist_grad[~col_mask] = 0.0 - hist.hist_hess[~col_mask] = 0.0 - - # Find splits for all nodes (with missing/categorical handling) - splits = find_node_splits( - histograms, - reg_lambda=config.reg_lambda, - min_child_weight=config.min_child_weight, - min_gain=config.min_gain, - has_missing=has_missing, # Phase 14 - is_categorical=is_categorical, # Phase 14.3 - n_categories=n_categories, # Phase 14.3 - ) - - # Only update depth if at least one valid split was found - if splits: - actual_depth = depth + 1 - - # Apply splits to tree structure - for node_id, node_split in splits.items(): - features[node_id] = node_split.split.feature - thresholds[node_id] = node_split.split.threshold - left_children[node_id] = node_split.left_child - right_children[node_id] = node_split.right_child - missing_go_left[node_id] = node_split.missing_go_left # Phase 14 - is_categorical_split[node_id] = node_split.is_categorical # Phase 14.3 - cat_bitsets[node_id] = node_split.cat_bitset # Phase 14.3 - - # Partition samples (handles missing via learned direction) - if splits: - sample_node_ids = partition_samples( - binned, sample_node_ids, splits, - missing_go_left=missing_go_left # Phase 14 - ) - - # Store histograms for subtraction at next level - parent_histograms = histograms - - # Compute leaf values for all leaf nodes - leaf_nodes = self._find_leaf_nodes(features, left_children, max_nodes) - leaf_values = compute_leaf_values( - grad, hess, sample_node_ids, leaf_nodes, config.reg_lambda, config.reg_alpha - ) - - for node_id, value in leaf_values.items(): - values[node_id] = value - - # Trim to actual size - n_nodes = self._count_nodes(left_children) - - # Check if we have any categorical splits - any_cat = np.any(is_categorical_split[:n_nodes]) if n_nodes > 0 else False - - return TreeStructure( - features=features[:n_nodes] if n_nodes < max_nodes else features, - thresholds=thresholds[:n_nodes] if n_nodes < max_nodes else thresholds, - values=values[:n_nodes] if n_nodes < max_nodes else values, - left_children=left_children[:n_nodes] if n_nodes < max_nodes else left_children, - right_children=right_children[:n_nodes] if n_nodes < max_nodes else right_children, - n_nodes=n_nodes, - depth=actual_depth, - n_features=n_features, - missing_go_left=missing_go_left[:n_nodes] if n_nodes < max_nodes else missing_go_left, # Phase 14 - is_categorical_split=is_categorical_split[:n_nodes] if any_cat else None, # Phase 14.3 - cat_bitsets=cat_bitsets[:n_nodes] if any_cat else None, # Phase 14.3 - ) - - def _plan_histogram_subtraction( - self, - active_nodes: list[int], - sample_node_ids, - parent_histograms: dict[int, NodeHistogram], - ) -> tuple[list[int], dict[int, tuple[NodeHistogram, int]]]: - """Plan which children to build vs subtract. - - For each parent that split, build the histogram for the smaller child - and derive the larger child via subtraction: larger = parent - smaller. - This halves histogram computation on average. - - Returns: - build_nodes: Nodes whose histograms must be built from samples. - subtract_info: {child_id: (parent_histogram, sibling_id_to_subtract_from)} - """ - if hasattr(sample_node_ids, 'copy_to_host'): - ids_cpu = sample_node_ids.copy_to_host() - else: - ids_cpu = np.asarray(sample_node_ids) - - # Count samples per node - node_counts: dict[int, int] = {} - for nid in active_nodes: - node_counts[nid] = int(np.sum(ids_cpu == nid)) - - build_nodes: list[int] = [] - subtract_info: dict[int, tuple[NodeHistogram, int]] = {} - - # Group children by parent - processed_parents: set[int] = set() - for nid in active_nodes: - parent_id = (nid - 1) // 2 - if parent_id in processed_parents: - continue - if parent_id not in parent_histograms: - # No parent histogram — must build from samples - build_nodes.append(nid) - continue - - # Find sibling - left_child = 2 * parent_id + 1 - right_child = 2 * parent_id + 2 - sibling = right_child if nid == left_child else left_child - - # Both children must be active for subtraction - if sibling not in node_counts: - build_nodes.append(nid) - continue - - processed_parents.add(parent_id) - parent_hist = parent_histograms[parent_id] - - # Build the smaller child, subtract for the larger - if node_counts[left_child] <= node_counts[right_child]: - build_nodes.append(left_child) - subtract_info[right_child] = (parent_hist, left_child) - else: - build_nodes.append(right_child) - subtract_info[left_child] = (parent_hist, right_child) - - return build_nodes, subtract_info - - def _get_active_nodes(self, sample_node_ids, candidate_nodes: list[int]) -> list[int]: - """Get nodes that have samples assigned to them.""" - if hasattr(sample_node_ids, 'copy_to_host'): - sample_node_ids = sample_node_ids.copy_to_host() - unique_nodes = set(np.unique(sample_node_ids)) - return [n for n in candidate_nodes if n in unique_nodes] - - def _find_leaf_nodes(self, features, left_children, max_nodes) -> list[int]: - """Find all leaf nodes (nodes with no children or not split).""" - leaves = [] - for i in range(max_nodes): - if left_children[i] == -1 and (i == 0 or self._has_parent(i, features)): - leaves.append(i) - return leaves - - def _has_parent(self, node_id: int, features) -> bool: - """Check if node has a valid parent (was created by a split).""" - if node_id == 0: - return True - parent = (node_id - 1) // 2 - return features[parent] >= 0 - - def _count_nodes(self, left_children) -> int: - """Count actual nodes in tree by walking from highest index down.""" - # Find the highest valid node index: a node is valid if it's the root - # or its parent was split (parent has left_children != -1). - for i in range(len(left_children) - 1, -1, -1): - if i == 0: - return 1 - parent = (i - 1) // 2 - if left_children[parent] != -1: - # This node is valid — the tree size is at least i+1 - return i + 1 - return 1 - - -# ============================================================================= -# Leaf-Wise Growth (LightGBM style) -# ============================================================================= - -class LeafWiseGrowth(GrowthStrategy): - """Leaf-wise (best-first) tree growth. - - Always splits the leaf with the highest gain, regardless of depth. - This is the strategy used by LightGBM. - - Characteristics: - - Unbalanced trees (deeper on informative branches) - - Often achieves lower loss with fewer leaves - - Can overfit if max_leaves not set properly - - More kernel launches on GPU (one per split) - - Note: - Requires `config.max_leaves` to be set. - """ - - def grow( - self, - binned: NDArray, - grad: NDArray, - hess: NDArray, - config: GrowthConfig, - has_missing: NDArray | None = None, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, - ) -> TreeStructure: - """Grow tree by always splitting best leaf.""" - # Compute locally instead of mutating the shared config object - max_leaves = config.max_leaves if config.max_leaves is not None else 2**config.max_depth - - n_features, n_samples = binned.shape - # Use complete binary tree size to accommodate any depth - # Node IDs follow binary tree convention, so we need 2^(max_depth+1) - 1 slots - max_nodes = 2**(config.max_depth + 1) - 1 - - # Initialize arrays - features = np.full(max_nodes, -1, dtype=np.int32) - thresholds = np.zeros(max_nodes, dtype=np.int32) - values = np.zeros(max_nodes, dtype=np.float32) - left_children = np.full(max_nodes, -1, dtype=np.int32) - right_children = np.full(max_nodes, -1, dtype=np.int32) - - # Track sample assignments (CPU for leaf-wise since we need frequent access) - sample_node_ids = np.zeros(n_samples, dtype=np.int32) - - # Priority queue: (negative_gain, node_id, split_info, histogram) - # We use negative gain because heapq is min-heap - import heapq - candidates: list[tuple[float, int, NodeSplit, NodeHistogram]] = [] - - # Start with root - root_hist = build_node_histograms(binned, grad, hess, sample_node_ids, [0]) - if 0 not in root_hist: - # No samples, return single leaf - return self._single_leaf_tree(grad, hess, config, n_features) - - root_splits = find_node_splits( - root_hist, - reg_lambda=config.reg_lambda, - min_child_weight=config.min_child_weight, - min_gain=config.min_gain, - has_missing=has_missing, - is_categorical=is_categorical, - n_categories=n_categories, - ) - - if 0 in root_splits: - heapq.heappush(candidates, ( - -root_splits[0].split.gain, - 0, - root_splits[0], - root_hist[0], - )) - - n_leaves = 1 - actual_depth = 0 - - while candidates and n_leaves < max_leaves: - neg_gain, node_id, node_split, node_hist = heapq.heappop(candidates) - - # Check if this node is still a leaf (might have been split) - if features[node_id] >= 0: - continue - - # Apply split - features[node_id] = node_split.split.feature - thresholds[node_id] = node_split.split.threshold - left_children[node_id] = node_split.left_child - right_children[node_id] = node_split.right_child - - # Update sample assignments - sample_node_ids = partition_samples( - binned, sample_node_ids, {node_id: node_split} - ) - if hasattr(sample_node_ids, 'copy_to_host'): - sample_node_ids = sample_node_ids.copy_to_host() - - n_leaves += 1 # Split creates one new leaf (2 children - 1 parent) - - # Track depth - node_depth = self._get_depth(node_id) - actual_depth = max(actual_depth, node_depth + 1) - - # Don't exceed max_depth - if node_depth + 1 >= config.max_depth: - continue - - # Find splits for children - left_id, right_id = node_split.left_child, node_split.right_child - child_hists = build_node_histograms( - binned, grad, hess, sample_node_ids, [left_id, right_id] - ) - - child_splits = find_node_splits( - child_hists, - reg_lambda=config.reg_lambda, - min_child_weight=config.min_child_weight, - min_gain=config.min_gain, - has_missing=has_missing, - is_categorical=is_categorical, - n_categories=n_categories, - ) - - # Add valid child splits to candidates - for child_id in [left_id, right_id]: - if child_id in child_splits and child_id in child_hists: - heapq.heappush(candidates, ( - -child_splits[child_id].split.gain, - child_id, - child_splits[child_id], - child_hists[child_id], - )) - - # Compute leaf values - leaf_nodes = [i for i in range(max_nodes) if left_children[i] == -1 and - (i == 0 or features[(i-1)//2] >= 0)] - leaf_values = compute_leaf_values( - grad, hess, sample_node_ids, leaf_nodes, config.reg_lambda, config.reg_alpha - ) - - for node_id, value in leaf_values.items(): - values[node_id] = value - - return TreeStructure( - features=features, - thresholds=thresholds, - values=values, - left_children=left_children, - right_children=right_children, - n_nodes=max_nodes, - depth=actual_depth, - n_features=n_features, - ) - - def _get_depth(self, node_id: int) -> int: - """Get depth of a node.""" - if node_id == 0: - return 0 - return int(np.floor(np.log2(node_id + 1))) - - def _single_leaf_tree(self, grad, hess, config, n_features) -> TreeStructure: - """Create a tree with just the root as a leaf.""" - if hasattr(grad, 'copy_to_host'): - grad = grad.copy_to_host() - hess = hess.copy_to_host() - - denom = float(np.sum(hess)) + config.reg_lambda - leaf_value = -float(np.sum(grad)) / denom if denom != 0.0 else 0.0 - - return TreeStructure( - features=np.array([-1], dtype=np.int32), - thresholds=np.array([0], dtype=np.int32), - values=np.array([leaf_value], dtype=np.float32), - left_children=np.array([-1], dtype=np.int32), - right_children=np.array([-1], dtype=np.int32), - n_nodes=1, - depth=0, - n_features=n_features, - ) - - -# ============================================================================= -# Symmetric Growth (CatBoost style) -# ============================================================================= - -class SymmetricGrowth(GrowthStrategy): - """Symmetric (oblivious) tree growth. - - All nodes at the same depth use the SAME split condition. - This is the strategy used by CatBoost. - - Characteristics: - - Very fast prediction (just bit operations) - - Regularization effect (fewer parameters) - - 2^depth leaves regardless of data - - Good for categorical features - - The level split is chosen leaf-aware (CatBoost-style): for each candidate - (feature, threshold), the gain is the SUM of per-leaf gains computed from - each leaf's own histogram. Summing histograms across leaves before split - finding (the previous implementation) collapses to the root histogram — - identical at every depth — and repeats the same split at every level. - - Missing values (bin 255) always route right, both here and in prediction - (ordinal: 255 > any threshold; categorical: bins >= 64 are outside the - bitset), so training and prediction stay consistent. - """ - - def grow( - self, - binned: NDArray, - grad: NDArray, - hess: NDArray, - config: GrowthConfig, - has_missing: NDArray | None = None, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, - ) -> TreeStructure: - """Grow symmetric tree with one split per level.""" - # Symmetric aggregation is host-side; copy once up front. - if hasattr(binned, 'copy_to_host'): - binned = binned.copy_to_host() - if hasattr(grad, 'copy_to_host'): - grad = grad.copy_to_host() - if hasattr(hess, 'copy_to_host'): - hess = hess.copy_to_host() - binned = np.asarray(binned) - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - - n_features, n_samples = binned.shape - - level_features = np.full(config.max_depth, -1, dtype=np.int32) - level_thresholds = np.zeros(config.max_depth, dtype=np.int32) - level_is_cat = np.zeros(config.max_depth, dtype=np.bool_) - level_bitsets = np.zeros(config.max_depth, dtype=np.uint64) - - # Track sample leaf assignments (0 to 2^depth - 1) - sample_leaf_ids = np.zeros(n_samples, dtype=np.int32) - - actual_depth = 0 - - for depth in range(config.max_depth): - level_split = self._find_level_split( - binned, grad, hess, sample_leaf_ids, config, - is_categorical=is_categorical, - n_categories=n_categories, - ) - if level_split is None: - break - - feature, threshold, is_cat, cat_bitset = level_split - level_features[depth] = feature - level_thresholds[depth] = threshold - level_is_cat[depth] = is_cat - level_bitsets[depth] = cat_bitset - actual_depth = depth + 1 - - # Partition ALL samples using this single split. - feature_values = binned[feature, :] - if is_cat: - # Bitset membership goes left; bins >= 64 (incl. missing 255) - # are not representable in the 64-bit set and go right — - # matching _partition_samples_cpu and prediction. - left_lut = np.zeros(256, dtype=np.bool_) - for c in range(64): - left_lut[c] = (cat_bitset >> c) & 1 - goes_right = ~left_lut[feature_values] - else: - # Missing (bin 255) > any threshold (<= 254) → right. - goes_right = feature_values > threshold - sample_leaf_ids = 2 * sample_leaf_ids + goes_right.astype(np.int32) - - # Compute leaf values for all 2^depth leaves - n_leaves = 2**actual_depth - leaf_values = np.zeros(n_leaves, dtype=np.float32) - - grad_cpu = grad - hess_cpu = hess - - for leaf_id in range(n_leaves): - mask = sample_leaf_ids == leaf_id - if np.any(mask): - sum_grad = float(np.sum(grad_cpu[mask])) - sum_hess = float(np.sum(hess_cpu[mask])) - # Apply L1 soft-thresholding (same pattern as compute_leaf_values in _split.py) - if config.reg_alpha > 0: - if sum_grad > config.reg_alpha: - leaf_values[leaf_id] = -(sum_grad - config.reg_alpha) / (sum_hess + config.reg_lambda) - elif sum_grad < -config.reg_alpha: - leaf_values[leaf_id] = -(sum_grad + config.reg_alpha) / (sum_hess + config.reg_lambda) - else: - leaf_values[leaf_id] = 0.0 - else: - leaf_values[leaf_id] = -sum_grad / (sum_hess + config.reg_lambda) - - # Create TreeStructure with symmetric flag - # For compatibility, also create standard tree arrays - max_nodes = 2**(actual_depth + 1) - 1 - features = np.full(max_nodes, -1, dtype=np.int32) - thresholds = np.zeros(max_nodes, dtype=np.int32) - values = np.zeros(max_nodes, dtype=np.float32) - left_children = np.full(max_nodes, -1, dtype=np.int32) - right_children = np.full(max_nodes, -1, dtype=np.int32) - is_categorical_split = np.zeros(max_nodes, dtype=np.bool_) - cat_bitsets = np.zeros(max_nodes, dtype=np.uint64) - - # Fill in symmetric structure - self._fill_symmetric_structure( - features, thresholds, left_children, right_children, - level_features, level_thresholds, actual_depth, - is_categorical_split=is_categorical_split, - cat_bitsets=cat_bitsets, - level_is_cat=level_is_cat, - level_bitsets=level_bitsets, - ) - - # Set leaf values - leaf_start = 2**actual_depth - 1 - for i, val in enumerate(leaf_values): - values[leaf_start + i] = val - - # The fast symmetric prediction path only supports ordinal comparisons. - # When a level uses a categorical (bitset) split, return a standard tree - # (structurally still symmetric) so prediction routes by set membership. - any_cat_split = bool(np.any(level_is_cat[:actual_depth])) - - return TreeStructure( - features=features, - thresholds=thresholds, - values=values, - left_children=left_children, - right_children=right_children, - n_nodes=max_nodes, - depth=actual_depth, - n_features=n_features, - is_symmetric=not any_cat_split, - level_features=level_features[:actual_depth], - level_thresholds=level_thresholds[:actual_depth], - is_categorical_split=is_categorical_split if any_cat_split else None, - cat_bitsets=cat_bitsets if any_cat_split else None, - ) - - def _find_level_split( - self, - binned: NDArray, - grad: NDArray, - hess: NDArray, - sample_leaf_ids: NDArray, - config: GrowthConfig, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, - ) -> tuple[int, int, bool, int] | None: - """Find the single best split shared by all leaves at this level. - - For each candidate split, the total gain is the sum of per-leaf gains - computed from each leaf's own histogram (leaves whose children would - violate ``min_child_weight`` contribute zero). This is what makes the - oblivious tree non-degenerate: a shared histogram would be identical - at every depth. - - Returns: - (feature, threshold, is_categorical, cat_bitset) or None if no - candidate achieves positive gain above ``config.min_gain``. - For categorical splits ``threshold`` is the split point in the - pooled Fisher ordering and ``cat_bitset`` holds the categories - (< 64) that go left. - """ - reg_lambda = config.reg_lambda - mcw = config.min_child_weight - n_features = binned.shape[0] - - active_leaves = [int(leaf) for leaf in np.unique(sample_leaf_ids)] - leaf_hists = build_node_histograms( - binned, grad, hess, sample_leaf_ids, active_leaves - ) - if not leaf_hists: - return None - - any_cat = is_categorical is not None and np.any(is_categorical) - - # Aggregate ordinal candidate gains across leaves. Threshold t means - # bins <= t go left; the missing bin (255) always stays right. - total_gain = np.zeros((n_features, 255), dtype=np.float64) - if any_cat: - pooled_grad = np.zeros((n_features, 256), dtype=np.float64) - pooled_hess = np.zeros((n_features, 256), dtype=np.float64) - - splittable_hists = [] - for hist in leaf_hists.values(): - if hist.n_samples == 0 or hist.sum_hess < mcw: - continue # Leaf cannot be validly split; contributes no gain. - splittable_hists.append(hist) - hg = np.asarray(hist.hist_grad, dtype=np.float64) - hh = np.asarray(hist.hist_hess, dtype=np.float64) - left_g = np.cumsum(hg[:, :255], axis=1) - left_h = np.cumsum(hh[:, :255], axis=1) - tot_g = left_g[:, -1:] + hg[:, 255:256] # includes missing bin - tot_h = left_h[:, -1:] + hh[:, 255:256] - right_g = tot_g - left_g - right_h = tot_h - left_h - with np.errstate(divide='ignore', invalid='ignore'): - gain = ( - left_g**2 / (left_h + reg_lambda) - + right_g**2 / (right_h + reg_lambda) - - tot_g**2 / (tot_h + reg_lambda) - ) - gain = np.nan_to_num(gain, nan=0.0, posinf=0.0, neginf=0.0) - valid = (left_h >= mcw) & (right_h >= mcw) - total_gain += np.where(valid, gain, 0.0) - if any_cat: - pooled_grad += hg - pooled_hess += hh - - if not splittable_hists: - return None - - # Ordinal splits are meaningless for categorical features. - if any_cat: - cat_mask = np.asarray(is_categorical, dtype=np.bool_) - total_gain[cat_mask, :] = -np.inf - - flat_best = int(np.argmax(total_gain)) - best_feature, best_threshold = divmod(flat_best, total_gain.shape[1]) - best = ( - float(total_gain[best_feature, best_threshold]), - int(best_feature), - int(best_threshold), - False, - 0, - ) - - # Categorical candidates: one shared Fisher ordering (from the pooled - # per-leaf histograms, since all leaves share the split), scanned as - # prefixes with per-leaf gains aggregated across leaves. - if any_cat: - for f in np.flatnonzero(cat_mask): - n_cats = int(n_categories[f]) if n_categories is not None else 0 - if n_cats < 2: - continue - pg = pooled_grad[f, :n_cats] - ph = pooled_hess[f, :n_cats] - scores = np.where(ph > 1e-10, -pg / (ph + reg_lambda), 0.0) - order = np.argsort(scores, kind='stable') - - agg = np.zeros(n_cats - 1, dtype=np.float64) - for hist in splittable_hists: - hg = np.asarray(hist.hist_grad[f], dtype=np.float64) - hh = np.asarray(hist.hist_hess[f], dtype=np.float64) - left_g = np.cumsum(hg[order])[:-1] - left_h = np.cumsum(hh[order])[:-1] - tot_g = hg[:n_cats].sum() + hg[255] # missing goes right - tot_h = hh[:n_cats].sum() + hh[255] - right_g = tot_g - left_g - right_h = tot_h - left_h - with np.errstate(divide='ignore', invalid='ignore'): - gain = ( - left_g**2 / (left_h + reg_lambda) - + right_g**2 / (right_h + reg_lambda) - - tot_g**2 / (tot_h + reg_lambda) - ) - gain = np.nan_to_num(gain, nan=0.0, posinf=0.0, neginf=0.0) - valid = (left_h >= mcw) & (right_h >= mcw) - agg += np.where(valid, gain, 0.0) - - k = int(np.argmax(agg)) - if agg[k] > best[0]: - # Same convention as _find_best_categorical_split: bins - # >= 64 cannot be represented and always route right. - bitset = 0 - for c in order[:k + 1]: - if c < 64: - bitset |= 1 << int(c) - best = (float(agg[k]), int(f), k + 1, True, bitset) - - best_gain, feature, threshold, is_cat, cat_bitset = best - if best_gain <= 0.0 or best_gain < config.min_gain: - return None - return feature, threshold, is_cat, cat_bitset - - def _fill_symmetric_structure( - self, - features, - thresholds, - left_children, - right_children, - level_features, - level_thresholds, - depth, - is_categorical_split=None, - cat_bitsets=None, - level_is_cat=None, - level_bitsets=None, - ): - """Fill standard tree arrays from symmetric representation.""" - for d in range(depth): - feat = level_features[d] - thresh = level_thresholds[d] - - # All nodes at this depth get same split - level_start = 2**d - 1 - level_end = 2**(d+1) - 1 - - for node in range(level_start, level_end): - features[node] = feat - thresholds[node] = thresh - left_children[node] = 2 * node + 1 - right_children[node] = 2 * node + 2 - if is_categorical_split is not None and level_is_cat is not None: - is_categorical_split[node] = level_is_cat[d] - cat_bitsets[node] = level_bitsets[d] - - -# ============================================================================= -# Registry / Factory Function -# ============================================================================= - -# name (lowercase) -> strategy class. Seeded with the built-in strategies and -# their aliases; extended at runtime via ``register_growth_strategy``. -_GROWTH_REGISTRY: dict[str, type[GrowthStrategy]] = { - "levelwise": LevelWiseGrowth, - "level_wise": LevelWiseGrowth, - "level-wise": LevelWiseGrowth, - "leafwise": LeafWiseGrowth, - "leaf_wise": LeafWiseGrowth, - "leaf-wise": LeafWiseGrowth, - "symmetric": SymmetricGrowth, - "oblivious": SymmetricGrowth, -} - - -def register_growth_strategy( - name: str, - cls: type[GrowthStrategy], - *, - override: bool = False, -) -> type[GrowthStrategy]: - """Register a custom growth strategy class under a string name. - - After registration the name works everywhere a built-in growth name does, - e.g. ``GradientBoosting(growth='mygrowth')`` or - ``fit_tree(X, grad, hess, growth='mygrowth')``. - - Args: - name: Name to register the strategy under. Lookup is - case-insensitive (the name is stored lowercased, matching - ``get_growth_strategy``). - cls: A ``GrowthStrategy`` subclass. It is instantiated with no - arguments each time the name is resolved. - override: Pass True to replace an existing registration (including - a built-in alias). Without it a duplicate name raises ValueError. - - Returns: - ``cls`` unchanged (usable as a decorator via functools.partial). - """ - if not isinstance(name, str) or not name: - raise TypeError(f"Growth strategy name must be a non-empty string, got {name!r}") - if not (isinstance(cls, type) and issubclass(cls, GrowthStrategy)): - raise TypeError( - f"Growth strategy must be a GrowthStrategy subclass, got {cls!r}" - ) - key = name.lower() - if not override and key in _GROWTH_REGISTRY: - raise ValueError( - f"Growth strategy '{key}' is already registered. " - "Pass override=True to replace it." - ) - _GROWTH_REGISTRY[key] = cls - return cls - - -def get_growth_strategy(name: str) -> GrowthStrategy: - """Get a growth strategy by name. - - Args: - name: Strategy name - "levelwise", "leafwise", "symmetric", or any - name registered via ``register_growth_strategy`` - - Returns: - GrowthStrategy instance - """ - name_lower = name.lower() - if name_lower not in _GROWTH_REGISTRY: - available = sorted(_GROWTH_REGISTRY) - raise ValueError(f"Unknown growth strategy '{name}'. Available: {available}") - - return _GROWTH_REGISTRY[name_lower]() diff --git a/src/openboost/_core/_histogram.py b/src/openboost/_core/_histogram.py deleted file mode 100644 index 135abf3..0000000 --- a/src/openboost/_core/_histogram.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Histogram building for gradient boosting. - -Dispatches to CUDA or CPU backend based on configuration. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -import numpy as np - -from .._backends import is_cuda - -if TYPE_CHECKING: - from numpy.typing import NDArray - - from .._array import BinnedArray - - -def build_histogram( - binned: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - sample_indices: NDArray | None = None, -) -> tuple[NDArray, NDArray]: - """Build gradient and hessian histograms. - - Args: - binned: Binned feature data (BinnedArray or raw array) - grad: Gradient vector, shape (n_samples,) or (n_subset,) - hess: Hessian vector, shape (n_samples,) or (n_subset,) - sample_indices: Optional subset of samples to use - - Returns: - hist_grad: Shape (n_features, 256), float64 - hist_hess: Shape (n_features, 256), float64 - """ - # Extract raw data if BinnedArray - from .._array import BinnedArray - if isinstance(binned, BinnedArray): - binned_data = binned.data - device = binned.device - else: - binned_data = binned - device = "cuda" if is_cuda() and hasattr(binned_data, '__cuda_array_interface__') else "cpu" - - # Handle sample subsetting - if sample_indices is not None: - # Subset the data (needed for node-specific histograms) - if device == "cuda": - from .._backends._cuda import gather_cuda - binned_data = gather_cuda(binned_data, sample_indices) - grad = gather_cuda(grad, sample_indices) - hess = gather_cuda(hess, sample_indices) - else: - binned_data = binned_data[:, sample_indices] - grad = grad[sample_indices] - hess = hess[sample_indices] - - # Dispatch to backend - if device == "cuda" and is_cuda(): - from .._backends._cuda import build_histogram_cuda - return build_histogram_cuda(binned_data, grad, hess) - else: - from .._backends._cpu import build_histogram_cpu - # Ensure numpy arrays for CPU backend - if hasattr(binned_data, 'copy_to_host'): - binned_data = binned_data.copy_to_host() - if hasattr(grad, 'copy_to_host'): - grad = grad.copy_to_host() - if hasattr(hess, 'copy_to_host'): - hess = hess.copy_to_host() - return build_histogram_cpu( - np.asarray(binned_data), - np.asarray(grad, dtype=np.float32), - np.asarray(hess, dtype=np.float32), - ) - - -def subtract_histogram( - parent_grad: NDArray, - parent_hess: NDArray, - child_grad: NDArray, - child_hess: NDArray, -) -> tuple[NDArray, NDArray]: - """Compute sibling histogram via subtraction. - - sibling = parent - child - - This is O(n_features * 256) instead of O(n_features * n_samples), - giving ~2x speedup on histogram building. - - Args: - parent_grad, parent_hess: Parent node histograms - child_grad, child_hess: One child's histograms - - Returns: - sibling_grad, sibling_hess: Other child's histograms - """ - # Check if we're dealing with CUDA arrays - if hasattr(parent_grad, '__cuda_array_interface__'): - # Use CUDA subtraction kernel - from .._backends._cuda import subtract_histograms_cuda - return subtract_histograms_cuda(parent_grad, parent_hess, child_grad, child_hess) - else: - # NumPy arrays - direct subtraction - sibling_grad = parent_grad - child_grad - sibling_hess = parent_hess - child_hess - return sibling_grad, sibling_hess - - -# Note: _subset_cuda removed in Phase 2, replaced by gather_cuda in _backends/_cuda.py - diff --git a/src/openboost/_core/_predict.py b/src/openboost/_core/_predict.py deleted file mode 100644 index 3f8f577..0000000 --- a/src/openboost/_core/_predict.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Prediction utilities for OpenBoost. - -This module provides prediction for ensembles of trees. -Single-tree prediction is in _tree.py. -""" - -from __future__ import annotations - -import contextlib -from typing import TYPE_CHECKING - -import numpy as np - -from .._array import BinnedArray -from .._backends import is_cuda - -if TYPE_CHECKING: - from numpy.typing import NDArray - - from ._tree import Tree - - -def predict_ensemble( - trees: list[Tree], - X: BinnedArray | NDArray, - learning_rate: float = 1.0, - init_score: float = 0.0, -) -> NDArray: - """Predict using an ensemble of trees. - - Args: - trees: List of fitted Tree objects - X: BinnedArray or binned data - learning_rate: Learning rate to apply to each tree - init_score: Initial prediction value - - Returns: - predictions: Shape (n_samples,) - """ - if isinstance(X, BinnedArray): - n_samples = X.n_samples - device = X.device - else: - n_samples = X.shape[1] - device = "cuda" if is_cuda() and hasattr(X, '__cuda_array_interface__') else "cpu" - - # Initialize predictions - if device == "cuda" and is_cuda(): - from numba import cuda - pred = cuda.device_array(n_samples, dtype=np.float32) - # Initialize to init_score - _fill_cuda(pred, init_score) - else: - pred = np.full(n_samples, init_score, dtype=np.float32) - - # Accumulate tree predictions - for tree in trees: - tree_pred = tree(X) - if device == "cuda" and is_cuda(): - _add_inplace_cuda(pred, tree_pred, learning_rate) - else: - pred += learning_rate * tree_pred - - return pred - - -# Module-level CUDA kernels to avoid recompilation on every call -_fill_cuda_kernel = None -_add_inplace_cuda_kernel = None - - -def _get_fill_cuda_kernel(): - """Get or compile the fill kernel.""" - global _fill_cuda_kernel - if _fill_cuda_kernel is not None: - return _fill_cuda_kernel - - from numba import cuda - - @cuda.jit - def kernel(arr, val): - idx = cuda.grid(1) - if idx < arr.shape[0]: - arr[idx] = val - - _fill_cuda_kernel = kernel - return kernel - - -def _get_add_inplace_cuda_kernel(): - """Get or compile the add-inplace kernel.""" - global _add_inplace_cuda_kernel - if _add_inplace_cuda_kernel is not None: - return _add_inplace_cuda_kernel - - from numba import cuda - - @cuda.jit - def kernel(arr, other, scale): - idx = cuda.grid(1) - if idx < arr.shape[0]: - arr[idx] += scale * other[idx] - - _add_inplace_cuda_kernel = kernel - return kernel - - -# Initialize kernels at module load if CUDA available -if is_cuda(): - try: - _fill_cuda_kernel = _get_fill_cuda_kernel() - _add_inplace_cuda_kernel = _get_add_inplace_cuda_kernel() - except Exception: - pass - - -def _fill_cuda(arr, value: float): - """Fill CUDA array with a value.""" - global _fill_cuda_kernel - if _fill_cuda_kernel is None: - _fill_cuda_kernel = _get_fill_cuda_kernel() - - threads = 256 - blocks = (len(arr) + threads - 1) // threads - _fill_cuda_kernel[blocks, threads](arr, value) - - -def _add_inplace_cuda(arr, other, scale: float): - """arr += scale * other (in-place on GPU).""" - global _add_inplace_cuda_kernel - if _add_inplace_cuda_kernel is None: - _add_inplace_cuda_kernel = _get_add_inplace_cuda_kernel() - - threads = 256 - blocks = (len(arr) + threads - 1) // threads - _add_inplace_cuda_kernel[blocks, threads](arr, other, scale) - - -# ============================================================================= -# Efficient In-Place Tree Prediction (Phase 5) -# ============================================================================= - -def predict_tree_add_gpu( - tree: Tree, - X: BinnedArray, - pred_gpu, - learning_rate: float = 1.0, -): - """Add tree predictions to pred_gpu in-place (no intermediate allocation). - - This is more efficient than tree(X) + add because it: - 1. Doesn't allocate a new array for tree predictions - 2. Fuses traversal and addition into a single kernel - 3. Uses GPU-resident tree arrays if available (Phase 5.1 - zero copy!) - - Args: - tree: Fitted Tree object - X: BinnedArray with binned features - pred_gpu: Device array to update in-place - learning_rate: Scale factor for predictions - """ - # Phase 5.1: Use to_gpu_arrays() which returns GPU arrays directly - # if tree was built with fit_tree_gpu_native() (zero copy!) - # Note: to_gpu_arrays() returns (features, thresholds, values, left, right) - node_features, node_thresholds, node_values, node_left, node_right = tree.to_gpu_arrays() - - # Get binned data - X_data = X.data if isinstance(X, BinnedArray) else X - n_samples = X.n_samples if isinstance(X, BinnedArray) else X.shape[1] - - threads = 256 - blocks = (n_samples + threads - 1) // threads - - # Ensure kernel is compiled (may be None if CUDA init failed at import) - global _predict_tree_add_kernel - if _predict_tree_add_kernel is None: - _predict_tree_add_kernel = _get_predict_tree_add_kernel() - - # Kernel expects: features, thresholds, left, right, values (match signature!) - _predict_tree_add_kernel[blocks, threads]( - X_data, node_features, node_thresholds, node_left, node_right, - node_values, pred_gpu, learning_rate, n_samples - ) - - -# Module-level kernel to avoid recompilation -_predict_tree_add_kernel = None - - -def _get_predict_tree_add_kernel(): - """Get or compile the predict-add kernel.""" - global _predict_tree_add_kernel - if _predict_tree_add_kernel is not None: - return _predict_tree_add_kernel - - from numba import cuda - - @cuda.jit - def kernel(X_binned, node_features, node_thresholds, node_left, node_right, - node_values, pred, learning_rate, n_samples): - idx = cuda.grid(1) - if idx < n_samples: - # Tree traversal - node = 0 - while node_features[node] >= 0: # Not a leaf - feat = node_features[node] - val = X_binned[feat, idx] # Feature-major layout - node = node_left[node] if val <= node_thresholds[node] else node_right[node] - - # Add leaf value to prediction - pred[idx] += learning_rate * node_values[node] - - _predict_tree_add_kernel = kernel - return kernel - - -# Initialize kernel at module load if CUDA available -if is_cuda(): - with contextlib.suppress(Exception): - _predict_tree_add_kernel = _get_predict_tree_add_kernel() - diff --git a/src/openboost/_core/_primitives.py b/src/openboost/_core/_primitives.py deleted file mode 100644 index 7672d91..0000000 --- a/src/openboost/_core/_primitives.py +++ /dev/null @@ -1,924 +0,0 @@ -"""Tree building primitives for OpenBoost. - -Phase 8.1: Extract reusable primitives that can be composed into different -tree growth strategies (level-wise, leaf-wise, symmetric). - -Phase 14: Added missing value handling - samples with bin 255 (NaN) are -routed according to the learned direction. - -These primitives operate on the `sample_node_ids` paradigm: -- Each sample is assigned to a node ID -- Histograms are built by aggregating samples per node -- Partitioning updates node IDs based on split decisions - -This design enables: -- Level-wise growth (XGBoost default): process all nodes at a depth -- Leaf-wise growth (LightGBM): process best leaf across all depths -- Symmetric growth (CatBoost): single split per depth level -""" - -from __future__ import annotations - -import warnings -from dataclasses import dataclass -from typing import TYPE_CHECKING - -import numpy as np - -from .._array import MISSING_BIN -from .._backends import is_cuda -from ._split import SplitInfo - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -# ============================================================================= -# Data Structures -# ============================================================================= - -@dataclass -class NodeHistogram: - """Histogram data for a single node.""" - node_id: int - hist_grad: NDArray # (n_features, 256) float32 - hist_hess: NDArray # (n_features, 256) float32 - sum_grad: float - sum_hess: float - n_samples: int - - -@dataclass -class NodeSplit: - """Split decision for a single node. - - Phase 14: Added missing_go_left for NaN handling. - Phase 14.3: Added categorical split info. - """ - node_id: int - split: SplitInfo - left_child: int # Node ID for left child (2 * node_id in binary tree) - right_child: int # Node ID for right child (2 * node_id + 1) - missing_go_left: bool = True # Direction for missing values (Phase 14) - is_categorical: bool = False # Phase 14.3: True if categorical split - cat_bitset: int = 0 # Phase 14.3: Bitmask for categories going left - - -# ============================================================================= -# Primitive: Build Histograms for Nodes -# ============================================================================= - -def build_node_histograms( - binned: NDArray, - grad: NDArray, - hess: NDArray, - sample_node_ids: NDArray, - node_ids: list[int], -) -> dict[int, NodeHistogram]: - """Build gradient/hessian histograms for specified nodes. - - This is the core primitive for tree building. It aggregates gradients - and hessians into 256-bin histograms for each feature, grouped by node. - - Args: - binned: Binned feature data, shape (n_features, n_samples), uint8 - grad: Gradients, shape (n_samples,), float32 - hess: Hessians, shape (n_samples,), float32 - sample_node_ids: Node assignment for each sample, shape (n_samples,), int32 - node_ids: List of node IDs to build histograms for - - Returns: - Dictionary mapping node_id -> NodeHistogram - - Example: - >>> # Build histograms for nodes at depth 2 (nodes 3, 4, 5, 6) - >>> histograms = build_node_histograms( - ... binned, grad, hess, sample_node_ids, - ... node_ids=[3, 4, 5, 6] - ... ) - >>> histograms[3].sum_grad # Total gradient in node 3 - """ - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - return _build_node_histograms_gpu(binned, grad, hess, sample_node_ids, node_ids) - return _build_node_histograms_cpu(binned, grad, hess, sample_node_ids, node_ids) - - -def _build_node_histograms_cpu( - binned: NDArray, - grad: NDArray, - hess: NDArray, - sample_node_ids: NDArray, - node_ids: list[int], -) -> dict[int, NodeHistogram]: - """CPU implementation of node histogram building.""" - from .._backends._cpu import build_histogram_cpu - - # Ensure numpy arrays - binned = np.asarray(binned) - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - sample_node_ids = np.asarray(sample_node_ids, dtype=np.int32) - - n_features = binned.shape[0] - result = {} - - for node_id in node_ids: - # Get samples belonging to this node - mask = sample_node_ids == node_id - n_samples = int(np.sum(mask)) - - if n_samples == 0: - # Empty node - create zero histogram - result[node_id] = NodeHistogram( - node_id=node_id, - hist_grad=np.zeros((n_features, 256), dtype=np.float32), - hist_hess=np.zeros((n_features, 256), dtype=np.float32), - sum_grad=0.0, - sum_hess=0.0, - n_samples=0, - ) - continue - - # Subset data for this node - node_binned = binned[:, mask] - node_grad = grad[mask] - node_hess = hess[mask] - - # Build histogram - hist_grad, hist_hess = build_histogram_cpu(node_binned, node_grad, node_hess) - - # Compute sums from histogram (sum across all bins of any feature) - sum_grad = float(np.sum(hist_grad[0])) - sum_hess = float(np.sum(hist_hess[0])) - - result[node_id] = NodeHistogram( - node_id=node_id, - hist_grad=hist_grad, - hist_hess=hist_hess, - sum_grad=sum_grad, - sum_hess=sum_hess, - n_samples=n_samples, - ) - - return result - - -def _build_node_histograms_gpu( - binned: NDArray, - grad: NDArray, - hess: NDArray, - sample_node_ids: NDArray, - node_ids: list[int], -) -> dict[int, NodeHistogram]: - """GPU implementation of node histogram building. - - Uses the optimized shared memory histogram kernel from Phase 6.3. - """ - - n_features, n_samples = binned.shape - - # Convert node_ids to contiguous range for kernel - # The kernel expects nodes in range [level_start, level_start + n_nodes) - # For arbitrary node_ids, we need to map them - - if not node_ids: - return {} - - # For efficiency, check if nodes are contiguous (common case: level-wise) - min_node = min(node_ids) - max_node = max(node_ids) - is_contiguous = (max_node - min_node + 1 == len(node_ids)) - - if is_contiguous and len(node_ids) <= 32: - # Fast path: use existing kernel directly - return _build_node_histograms_gpu_contiguous( - binned, grad, hess, sample_node_ids, min_node, len(node_ids) - ) - - # Slow path: build each node separately (for leaf-wise or sparse node sets) - return _build_node_histograms_gpu_sparse( - binned, grad, hess, sample_node_ids, node_ids - ) - - -def _build_node_histograms_gpu_contiguous( - binned, - grad, - hess, - sample_node_ids, - level_start: int, - n_nodes: int, -) -> dict[int, NodeHistogram]: - """GPU histogram building for contiguous node range.""" - import math - - from numba import cuda - - from .._backends._cuda import ( - _build_histogram_shared_kernel, - _zero_level_histograms_kernel, - ) - - n_features, n_samples = binned.shape - max_nodes = level_start + n_nodes - - # Allocate histogram storage - # Shape: (max_nodes, n_features, 256, 2) where [:,:,:,0]=grad, [:,:,:,1]=hess - histograms = cuda.device_array((max_nodes, n_features, 256, 2), dtype=np.float32) - - # Zero histograms - level_end = level_start + n_nodes - zero_grid = (n_nodes, n_features) - _zero_level_histograms_kernel[zero_grid, 256]( - histograms, level_start, level_end, n_features - ) - - # Build histograms using shared memory kernel - CHUNK_SIZE = 4096 - n_chunks = math.ceil(n_samples / CHUNK_SIZE) - hist_grid = (n_features, n_chunks) - - _no_const_hess = np.float32(0.0) - if n_nodes <= 16: - _build_histogram_shared_kernel[hist_grid, 256]( - binned, grad, hess, sample_node_ids, - level_start, n_nodes, 0, - histograms, _no_const_hess - ) - else: - # Two passes for >16 nodes - _build_histogram_shared_kernel[hist_grid, 256]( - binned, grad, hess, sample_node_ids, - level_start, 16, 0, - histograms, _no_const_hess - ) - remaining = n_nodes - 16 - _build_histogram_shared_kernel[hist_grid, 256]( - binned, grad, hess, sample_node_ids, - level_start, remaining, 16, - histograms, _no_const_hess - ) - - # Copy histograms to host and create NodeHistogram objects - histograms_cpu = histograms.copy_to_host() - sample_node_ids_cpu = ( - sample_node_ids.copy_to_host() - if hasattr(sample_node_ids, 'copy_to_host') - else np.asarray(sample_node_ids) - ) - - result = {} - for _i, node_id in enumerate(range(level_start, level_end)): - node_hist = histograms_cpu[node_id] - hist_grad = node_hist[:, :, 0] # (n_features, 256) - hist_hess = node_hist[:, :, 1] - - sum_grad = float(np.sum(hist_grad[0])) - sum_hess = float(np.sum(hist_hess[0])) - n_samples_node = int(np.sum(sample_node_ids_cpu == node_id)) - - result[node_id] = NodeHistogram( - node_id=node_id, - hist_grad=hist_grad.copy(), - hist_hess=hist_hess.copy(), - sum_grad=sum_grad, - sum_hess=sum_hess, - n_samples=n_samples_node, - ) - - return result - - -def _build_node_histograms_gpu_sparse( - binned, - grad, - hess, - sample_node_ids, - node_ids: list[int], -) -> dict[int, NodeHistogram]: - """GPU histogram building for non-contiguous nodes (leaf-wise).""" - from numba import cuda - - from .._backends._cuda import build_histogram_cuda, gather_cuda - - # For sparse node sets, build each node separately - # This is less efficient but works for any node configuration - - sample_node_ids_cpu = ( - sample_node_ids.copy_to_host() - if hasattr(sample_node_ids, 'copy_to_host') - else np.asarray(sample_node_ids) - ) - - result = {} - for node_id in node_ids: - mask = sample_node_ids_cpu == node_id - n_samples_node = int(np.sum(mask)) - - if n_samples_node == 0: - n_features = binned.shape[0] - result[node_id] = NodeHistogram( - node_id=node_id, - hist_grad=np.zeros((n_features, 256), dtype=np.float32), - hist_hess=np.zeros((n_features, 256), dtype=np.float32), - sum_grad=0.0, - sum_hess=0.0, - n_samples=0, - ) - continue - - # Get indices for this node - indices = np.where(mask)[0].astype(np.int32) - indices_gpu = cuda.to_device(indices) - - # Gather data for this node - node_binned = gather_cuda(binned, indices_gpu) - node_grad = gather_cuda(grad, indices_gpu) - node_hess = gather_cuda(hess, indices_gpu) - - # Build histogram - hist_grad, hist_hess = build_histogram_cuda(node_binned, node_grad, node_hess) - - # Copy to CPU - hist_grad_cpu = hist_grad.copy_to_host() - hist_hess_cpu = hist_hess.copy_to_host() - - sum_grad = float(np.sum(hist_grad_cpu[0])) - sum_hess = float(np.sum(hist_hess_cpu[0])) - - result[node_id] = NodeHistogram( - node_id=node_id, - hist_grad=hist_grad_cpu, - hist_hess=hist_hess_cpu, - sum_grad=sum_grad, - sum_hess=sum_hess, - n_samples=n_samples_node, - ) - - return result - - -# ============================================================================= -# Primitive: Histogram Subtraction -# ============================================================================= - -def subtract_histogram( - parent: NodeHistogram, - child: NodeHistogram, - sibling_node_id: int, -) -> NodeHistogram: - """Compute sibling histogram via subtraction: sibling = parent - child. - - This is O(n_features * 256) instead of O(n_features * n_samples), - giving ~2x speedup on histogram building when used with the - "build smaller child, subtract for larger" strategy. - - Args: - parent: Parent node histogram - child: One child's histogram - sibling_node_id: Node ID for the sibling - - Returns: - NodeHistogram for the sibling node - """ - sibling_grad = parent.hist_grad - child.hist_grad - sibling_hess = parent.hist_hess - child.hist_hess - - return NodeHistogram( - node_id=sibling_node_id, - hist_grad=sibling_grad, - hist_hess=sibling_hess, - sum_grad=parent.sum_grad - child.sum_grad, - sum_hess=parent.sum_hess - child.sum_hess, - n_samples=parent.n_samples - child.n_samples, - ) - - -# ============================================================================= -# Primitive: Find Splits for Nodes -# ============================================================================= - -def find_node_splits( - histograms: dict[int, NodeHistogram], - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - min_gain: float = 0.0, - has_missing: NDArray | None = None, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, -) -> dict[int, NodeSplit]: - """Find the best split for each node given histograms. - - Phase 14: Added has_missing parameter for NaN handling. - Phase 14.3: Added categorical feature support. - - Args: - histograms: Dictionary mapping node_id -> NodeHistogram - reg_lambda: L2 regularization term - min_child_weight: Minimum sum of hessian in each child - min_gain: Minimum gain required to split - has_missing: Boolean array (n_features,) indicating which features have NaN. - If None, standard split finding is used. - is_categorical: Boolean array (n_features,) indicating categorical features - n_categories: Number of categories per feature (0 for numeric) - - Returns: - Dictionary mapping node_id -> NodeSplit - Only includes nodes with valid splits (gain > min_gain). - - Example: - >>> histograms = build_node_histograms(...) - >>> splits = find_node_splits(histograms, reg_lambda=1.0) - >>> for node_id, split in splits.items(): - ... print(f"Node {node_id}: split on feature {split.split.feature}") - """ - from ._split import ( - find_best_split, - find_best_split_with_categorical, - find_best_split_with_missing, - ) - - result = {} - - # Check if we should use special split finding - use_missing = has_missing is not None and np.any(has_missing) - use_categorical = is_categorical is not None and np.any(is_categorical) - - for node_id, hist in histograms.items(): - # Skip empty nodes - if hist.n_samples == 0: - continue - - # Skip nodes that don't meet min_child_weight - if hist.sum_hess < min_child_weight: - continue - - # Find best split - if use_categorical: - # Phase 14.3: Use categorical-aware split finding - split = find_best_split_with_categorical( - hist.hist_grad, - hist.hist_hess, - hist.sum_grad, - hist.sum_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - has_missing=has_missing, - is_categorical=is_categorical, - n_categories=n_categories, - ) - elif use_missing: - split = find_best_split_with_missing( - hist.hist_grad, - hist.hist_hess, - hist.sum_grad, - hist.sum_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - has_missing=has_missing, - ) - else: - split = find_best_split( - hist.hist_grad, - hist.hist_hess, - hist.sum_grad, - hist.sum_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - ) - - if split.is_valid: - result[node_id] = NodeSplit( - node_id=node_id, - split=split, - left_child=2 * node_id + 1, # Binary tree indexing - right_child=2 * node_id + 2, - missing_go_left=split.missing_go_left, # Phase 14 - is_categorical=split.is_categorical, # Phase 14.3 - cat_bitset=split.cat_bitset, # Phase 14.3 - ) - - return result - - -# ============================================================================= -# Primitive: Partition Samples -# ============================================================================= - -def partition_samples( - binned: NDArray, - sample_node_ids: NDArray, - splits: dict[int, NodeSplit], - missing_go_left: NDArray | None = None, -) -> NDArray: - """Update sample node assignments based on split decisions. - - For each sample in a node that was split: - - If feature[split.feature] <= split.threshold: go to left child - - Otherwise: go to right child - - Phase 14: Missing values (bin 255) go according to learned direction - - Samples in nodes without splits remain unchanged. - - Args: - binned: Binned feature data, shape (n_features, n_samples), uint8 - sample_node_ids: Current node assignment, shape (n_samples,), int32 - splits: Dictionary of splits from find_node_splits() - missing_go_left: Boolean array (n_nodes,) for missing direction (Phase 14) - - Returns: - Updated sample_node_ids array (new array, original unchanged) - - Note: - This function returns a new array. For GPU, the returned array - is on GPU. For CPU, it's a numpy array. - """ - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - return _partition_samples_gpu(binned, sample_node_ids, splits, missing_go_left) - return _partition_samples_cpu(binned, sample_node_ids, splits, missing_go_left) - - -def _partition_samples_cpu( - binned: NDArray, - sample_node_ids: NDArray, - splits: dict[int, NodeSplit], - missing_go_left: NDArray | None = None, -) -> NDArray: - """CPU implementation of sample partitioning. - - Phase 14: Handles missing values (bin 255) using learned direction. - Phase 14.3: Handles categorical splits using bitmask membership. - """ - binned = np.asarray(binned) - sample_node_ids = np.asarray(sample_node_ids, dtype=np.int32) - - # Create output array - new_node_ids = sample_node_ids.copy() - - for node_id, node_split in splits.items(): - mask = sample_node_ids == node_id - if not np.any(mask): - continue - - feature = node_split.split.feature - threshold = node_split.split.threshold - - # Get feature values for samples in this node - feature_values = binned[feature, mask] - - # Phase 14: Handle missing values - is_missing = feature_values == MISSING_BIN - - # Phase 14.3: Determine routing based on split type - if node_split.is_categorical: - # Categorical split: use bitmask membership (int64 supports bins 0-63) - bitset = node_split.cat_bitset - goes_left = np.array( - [(bitset >> fv) & 1 if fv < 64 else 0 for fv in feature_values], - dtype=np.bool_, - ) - else: - # Ordinal split: use threshold - goes_left = feature_values <= threshold - - # Override for missing values using learned direction. - # Use the per-split learned direction as the primary source; - # only fall back to the tree-wide array if the split doesn't have the attribute. - if np.any(is_missing): - if hasattr(node_split, 'missing_go_left'): - miss_left = node_split.missing_go_left - elif missing_go_left is not None and node_id < len(missing_go_left): - miss_left = missing_go_left[node_id] - else: - miss_left = True - goes_left[is_missing] = miss_left - - # Update node IDs - sample_indices = np.where(mask)[0] - new_node_ids[sample_indices[goes_left]] = node_split.left_child - new_node_ids[sample_indices[~goes_left]] = node_split.right_child - - return new_node_ids - - -def _partition_samples_gpu( - binned, - sample_node_ids, - splits: dict[int, NodeSplit], - missing_go_left: NDArray | None = None, -) -> DeviceNDArray: - """GPU implementation of sample partitioning. - - Phase 14: Handles missing values (bin 255) using learned direction. - """ - import math - - from numba import cuda - - n_samples = sample_node_ids.shape[0] - - # Create output array on GPU - new_node_ids = cuda.device_array(n_samples, dtype=np.int32) - - # Copy current node IDs - ids_cpu = ( - sample_node_ids.copy_to_host() - if hasattr(sample_node_ids, 'copy_to_host') - else np.asarray(sample_node_ids) - ) - cuda.to_device(ids_cpu, to=new_node_ids) - - # Build arrays for kernel - if not splits: - return new_node_ids - - # Create split lookup arrays - max_node_id = max(splits.keys()) + 1 - split_features = np.full(max_node_id, -1, dtype=np.int32) - split_thresholds = np.full(max_node_id, 0, dtype=np.int32) - left_children = np.full(max_node_id, -1, dtype=np.int32) - right_children = np.full(max_node_id, -1, dtype=np.int32) - split_missing_left = np.ones(max_node_id, dtype=np.uint8) # Phase 14 - - for node_id, node_split in splits.items(): - split_features[node_id] = node_split.split.feature - split_thresholds[node_id] = node_split.split.threshold - left_children[node_id] = node_split.left_child - right_children[node_id] = node_split.right_child - split_missing_left[node_id] = 1 if node_split.missing_go_left else 0 # Phase 14 - - # Transfer to GPU - split_features_gpu = cuda.to_device(split_features) - split_thresholds_gpu = cuda.to_device(split_thresholds) - left_children_gpu = cuda.to_device(left_children) - right_children_gpu = cuda.to_device(right_children) - split_missing_left_gpu = cuda.to_device(split_missing_left) - - # Ensure kernel is compiled (lazy init if module-load compilation failed) - if _partition_kernel_with_missing is None: - _init_partition_kernel_with_missing() - - # Launch kernel - threads = 256 - blocks = math.ceil(n_samples / threads) - _partition_kernel_with_missing[blocks, threads]( - binned, sample_node_ids, new_node_ids, - split_features_gpu, split_thresholds_gpu, - left_children_gpu, right_children_gpu, - split_missing_left_gpu, - max_node_id, n_samples - ) - - return new_node_ids - - -# Partition kernel with missing value support (Phase 14) -_partition_kernel_with_missing = None - -def _init_partition_kernel_with_missing(): - global _partition_kernel_with_missing - if _partition_kernel_with_missing is not None: - return - - from numba import cuda, int32 - - @cuda.jit - def kernel(binned, old_node_ids, new_node_ids, - split_features, split_thresholds, left_children, right_children, - split_missing_left, max_node_id, n_samples): - """Partition kernel with missing value handling (Phase 14).""" - idx = cuda.grid(1) - if idx >= n_samples: - return - - node_id = old_node_ids[idx] - - # Check if this node was split - if node_id >= max_node_id or split_features[node_id] < 0: - new_node_ids[idx] = node_id - return - - feature = split_features[node_id] - threshold = split_thresholds[node_id] - bin_value = int32(binned[feature, idx]) - - # Phase 14: Handle missing values (bin 255) - if bin_value == 255: - # Use learned direction for missing - if split_missing_left[node_id] == 1: - new_node_ids[idx] = left_children[node_id] - else: - new_node_ids[idx] = right_children[node_id] - elif bin_value <= threshold: - new_node_ids[idx] = left_children[node_id] - else: - new_node_ids[idx] = right_children[node_id] - - _partition_kernel_with_missing = kernel - - -# Initialize kernel if CUDA available -if is_cuda(): - try: - _init_partition_kernel_with_missing() - except Exception: - warnings.warn("Failed to compile partition CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Primitive: Compute Leaf Values -# ============================================================================= - -def compute_leaf_values( - grad: NDArray, - hess: NDArray, - sample_node_ids: NDArray, - leaf_node_ids: list[int], - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, -) -> dict[int, float]: - """Compute optimal leaf values for specified nodes. - - Uses the Newton-Raphson optimal value with L1/L2 regularization. - - Without L1: -sum(grad) / (sum(hess) + lambda) - With L1: soft-thresholding applied to gradients - - Args: - grad: Gradients, shape (n_samples,), float32 - hess: Hessians, shape (n_samples,), float32 - sample_node_ids: Node assignment for each sample, shape (n_samples,), int32 - leaf_node_ids: List of node IDs to compute values for - reg_lambda: L2 regularization term - reg_alpha: L1 regularization term (Phase 11) - - Returns: - Dictionary mapping node_id -> leaf_value - """ - if is_cuda() and hasattr(grad, '__cuda_array_interface__'): - return _compute_leaf_values_gpu(grad, hess, sample_node_ids, leaf_node_ids, reg_lambda, reg_alpha) - return _compute_leaf_values_cpu(grad, hess, sample_node_ids, leaf_node_ids, reg_lambda, reg_alpha) - - -def _compute_leaf_values_cpu( - grad: NDArray, - hess: NDArray, - sample_node_ids: NDArray, - leaf_node_ids: list[int], - reg_lambda: float, - reg_alpha: float = 0.0, -) -> dict[int, float]: - """CPU implementation of leaf value computation.""" - from ._split import compute_leaf_value - - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - sample_node_ids = np.asarray(sample_node_ids, dtype=np.int32) - - result = {} - for node_id in leaf_node_ids: - mask = sample_node_ids == node_id - if not np.any(mask): - result[node_id] = 0.0 - continue - - sum_grad = float(np.sum(grad[mask])) - sum_hess = float(np.sum(hess[mask])) - - # Use shared leaf value computation (supports L1/L2) - result[node_id] = compute_leaf_value(sum_grad, sum_hess, reg_lambda, reg_alpha) - - return result - - -def _compute_leaf_values_gpu( - grad, - hess, - sample_node_ids, - leaf_node_ids: list[int], - reg_lambda: float, - reg_alpha: float = 0.0, -) -> dict[int, float]: - """GPU implementation of leaf value computation.""" - # For simplicity, copy to CPU and compute there - # Future optimization: use GPU reduction kernel - grad_cpu = grad.copy_to_host() if hasattr(grad, 'copy_to_host') else np.asarray(grad) - hess_cpu = hess.copy_to_host() if hasattr(hess, 'copy_to_host') else np.asarray(hess) - sample_node_ids_cpu = ( - sample_node_ids.copy_to_host() - if hasattr(sample_node_ids, 'copy_to_host') - else np.asarray(sample_node_ids) - ) - - return _compute_leaf_values_cpu( - grad_cpu, hess_cpu, sample_node_ids_cpu, leaf_node_ids, reg_lambda, reg_alpha - ) - - -# ============================================================================= -# Primitive: Initialize Sample Node IDs -# ============================================================================= - -def init_sample_node_ids(n_samples: int, device: str = "auto") -> NDArray: - """Initialize sample node IDs to root node (node 0). - - Args: - n_samples: Number of samples - device: "cpu", "cuda", or "auto" (detect from backend) - - Returns: - Array of zeros with shape (n_samples,), dtype int32 - """ - if device == "auto": - device = "cuda" if is_cuda() else "cpu" - - if device == "cuda": - from numba import cuda - arr = cuda.device_array(n_samples, dtype=np.int32) - # Zero-fill using kernel - threads = 256 - blocks = (n_samples + threads - 1) // threads - _zero_int_kernel[blocks, threads](arr, n_samples) - return arr - else: - return np.zeros(n_samples, dtype=np.int32) - - -_zero_int_kernel = None - -def _init_zero_kernel(): - global _zero_int_kernel - if _zero_int_kernel is not None: - return - - from numba import cuda - - @cuda.jit - def kernel(arr, n): - idx = cuda.grid(1) - if idx < n: - arr[idx] = 0 - - _zero_int_kernel = kernel - - -if is_cuda(): - try: - _init_zero_kernel() - except Exception: - warnings.warn("Failed to compile zero-fill CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Utility: Get Active Nodes -# ============================================================================= - -def get_nodes_at_depth(depth: int) -> list[int]: - """Get node IDs at a given depth (for level-wise growth). - - Uses binary tree indexing where: - - Root is node 0 (depth 0) - - Depth d has nodes [2^d - 1, 2^(d+1) - 1) - - Args: - depth: Tree depth (0 = root) - - Returns: - List of node IDs at that depth - """ - start = 2**depth - 1 - end = 2**(depth + 1) - 1 - return list(range(start, end)) - - -def get_children(node_id: int) -> tuple[int, int]: - """Get child node IDs for a given node. - - Uses binary tree indexing: - - Left child: 2 * node_id + 1 - - Right child: 2 * node_id + 2 - - Args: - node_id: Parent node ID - - Returns: - (left_child_id, right_child_id) - """ - return (2 * node_id + 1, 2 * node_id + 2) - - -def get_parent(node_id: int) -> int: - """Get parent node ID. - - Args: - node_id: Child node ID (must be > 0) - - Returns: - Parent node ID - """ - if node_id <= 0: - raise ValueError("Root node has no parent") - return (node_id - 1) // 2 diff --git a/src/openboost/_core/_split.py b/src/openboost/_core/_split.py deleted file mode 100644 index 029e0cd..0000000 --- a/src/openboost/_core/_split.py +++ /dev/null @@ -1,336 +0,0 @@ -"""Split finding for gradient boosting trees. - -Phase 14: Added missing value handling - learns optimal direction for NaN values. -Phase 14.3: Added native categorical feature support with Fisher-based splits. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, NamedTuple - -import numpy as np - -from .._backends import is_cuda - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -class SplitInfo(NamedTuple): - """Information about a split. - - For ordinal (numeric) splits: - - threshold: bin index, samples with bin <= threshold go left - - For categorical splits (Phase 14.3): - - is_categorical: True - - cat_bitset: uint64 bitmask, bit i=1 means category i goes left - """ - feature: int # Feature index (-1 if no valid split) - threshold: int # Bin threshold (ordinal) or split_point (categorical) - gain: float # Split gain - missing_go_left: bool = True # Direction for missing values (Phase 14) - is_categorical: bool = False # Phase 14.3: True if categorical split - cat_bitset: int = 0 # Phase 14.3: Bitmask for categories going left - cat_threshold: int = -1 # Phase 14.3: Split point in sorted category order - - @property - def is_valid(self) -> bool: - """Check if this is a valid split.""" - return self.feature >= 0 and self.gain > 0 - - -def find_best_split( - hist_grad: NDArray, - hist_hess: NDArray, - total_grad: float | None = None, - total_hess: float | None = None, - *, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - min_gain: float = 0.0, -) -> SplitInfo: - """Find the best split across all features. - - Args: - hist_grad: Gradient histogram, shape (n_features, 256) - hist_hess: Hessian histogram, shape (n_features, 256) - total_grad: Sum of gradients (computed if None) - total_hess: Sum of hessians (computed if None) - reg_lambda: L2 regularization term - min_child_weight: Minimum sum of hessian in each child - min_gain: Minimum gain to make a split - - Returns: - SplitInfo with best feature, threshold, and gain - """ - # Compute totals if not provided - if total_grad is None: - total_grad = float(_sum_histogram(hist_grad)) - if total_hess is None: - total_hess = float(_sum_histogram(hist_hess)) - - # Dispatch to backend - if is_cuda() and hasattr(hist_grad, '__cuda_array_interface__'): - from .._backends._cuda import find_best_split_cuda - feature, threshold, gain = find_best_split_cuda( - hist_grad, hist_hess, - total_grad, total_hess, - reg_lambda, min_child_weight, - ) - else: - from .._backends._cpu import find_best_split_cpu - # Ensure numpy for CPU - hist_grad_np = np.asarray(hist_grad.copy_to_host() if hasattr(hist_grad, 'copy_to_host') else hist_grad) - hist_hess_np = np.asarray(hist_hess.copy_to_host() if hasattr(hist_hess, 'copy_to_host') else hist_hess) - feature, threshold, gain = find_best_split_cpu( - hist_grad_np, hist_hess_np, - total_grad, total_hess, - reg_lambda, min_child_weight, - ) - - # Apply minimum gain threshold - if gain < min_gain: - return SplitInfo(feature=-1, threshold=-1, gain=0.0) - - return SplitInfo(feature=feature, threshold=threshold, gain=gain) - - -def compute_leaf_value( - sum_grad: float, - sum_hess: float, - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, -) -> float: - """Compute optimal leaf value with L1/L2 regularization. - - Without L1 (reg_alpha=0): - leaf_value = -sum_grad / (sum_hess + lambda) - - With L1 (reg_alpha > 0), uses soft-thresholding: - if |sum_grad| <= reg_alpha: return 0 - else: return -(sum_grad - sign(sum_grad)*reg_alpha) / (sum_hess + lambda) - - Args: - sum_grad: Sum of gradients in the leaf - sum_hess: Sum of hessians in the leaf - reg_lambda: L2 regularization - reg_alpha: L1 regularization (Phase 11) - - Returns: - Optimal leaf value - """ - # L1 soft-thresholding - if reg_alpha > 0.0: - if abs(sum_grad) <= reg_alpha: - return 0.0 - elif sum_grad > 0: - return -(sum_grad - reg_alpha) / (sum_hess + reg_lambda) - else: - return -(sum_grad + reg_alpha) / (sum_hess + reg_lambda) - else: - return -sum_grad / (sum_hess + reg_lambda) - - -def _sum_histogram(hist: NDArray) -> float: - """Sum values in a histogram (first feature only). - - hist is (n_features, n_bins); each feature has the same total, so - summing only the first avoids returning n_features * actual_total. - """ - if hasattr(hist, 'copy_to_host'): - hist = hist.copy_to_host() - return float(np.sum(hist[0])) - - -def find_best_split_with_missing( - hist_grad: NDArray, - hist_hess: NDArray, - total_grad: float | None = None, - total_hess: float | None = None, - *, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - min_gain: float = 0.0, - has_missing: NDArray | None = None, -) -> SplitInfo: - """Find the best split considering missing values. - - For each split candidate, tries both directions for missing values: - 1. Missing goes LEFT - 2. Missing goes RIGHT - - Picks whichever gives higher gain. - - Args: - hist_grad: Gradient histogram, shape (n_features, 256) - Bin 255 contains stats for missing values - hist_hess: Hessian histogram, shape (n_features, 256) - total_grad: Sum of gradients (computed if None) - total_hess: Sum of hessians (computed if None) - reg_lambda: L2 regularization term - min_child_weight: Minimum sum of hessian in each child - min_gain: Minimum gain to make a split - has_missing: Boolean array (n_features,) indicating which features have missing - - Returns: - SplitInfo with best feature, threshold, gain, and missing direction - """ - # Compute totals if not provided - if total_grad is None: - total_grad = float(_sum_histogram(hist_grad)) - if total_hess is None: - total_hess = float(_sum_histogram(hist_hess)) - - # Check if any feature has missing values - hist_grad.shape[0] - any_missing = has_missing is not None and np.any(has_missing) - - # If no missing values, use standard split finding - if not any_missing: - split = find_best_split( - hist_grad, hist_hess, total_grad, total_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - ) - return SplitInfo(split.feature, split.threshold, split.gain, True) - - # Dispatch to backend - if is_cuda() and hasattr(hist_grad, '__cuda_array_interface__'): - # Phase 14.2: Use GPU kernel for missing-aware split finding - from .._backends._cuda import find_best_split_with_missing_cuda - feature, threshold, gain, missing_go_left = find_best_split_with_missing_cuda( - hist_grad, hist_hess, - total_grad, total_hess, - reg_lambda, min_child_weight, - has_missing, - ) - else: - hist_grad_np = np.asarray(hist_grad.copy_to_host() if hasattr(hist_grad, 'copy_to_host') else hist_grad) - hist_hess_np = np.asarray(hist_hess.copy_to_host() if hasattr(hist_hess, 'copy_to_host') else hist_hess) - - from .._backends._cpu import find_best_split_with_missing_cpu - feature, threshold, gain, missing_go_left = find_best_split_with_missing_cpu( - hist_grad_np, hist_hess_np, - total_grad, total_hess, - reg_lambda, min_child_weight, - has_missing, - ) - - # Apply minimum gain threshold - if gain < min_gain: - return SplitInfo(feature=-1, threshold=-1, gain=0.0, missing_go_left=True) - - return SplitInfo(feature=feature, threshold=threshold, gain=gain, missing_go_left=missing_go_left) - - -# ============================================================================= -# Phase 14.3: Categorical Split Finding -# ============================================================================= - -def find_best_split_with_categorical( - hist_grad: NDArray, - hist_hess: NDArray, - total_grad: float | None = None, - total_hess: float | None = None, - *, - reg_lambda: float = 1.0, - min_child_weight: float = 1.0, - min_gain: float = 0.0, - has_missing: NDArray | None = None, - is_categorical: NDArray | None = None, - n_categories: NDArray | None = None, -) -> SplitInfo: - """Find the best split considering both categorical and missing values. - - For numeric features: ordinal splits (value <= threshold) - For categorical features: set membership splits via Fisher's optimal ordering - - Args: - hist_grad: Gradient histogram, shape (n_features, 256) - hist_hess: Hessian histogram, shape (n_features, 256) - total_grad: Sum of gradients (computed if None) - total_hess: Sum of hessians (computed if None) - reg_lambda: L2 regularization term - min_child_weight: Minimum sum of hessian in each child - min_gain: Minimum gain to make a split - has_missing: Boolean array (n_features,) for features with NaN - is_categorical: Boolean array (n_features,) for categorical features - n_categories: Number of categories per feature (0 for numeric) - - Returns: - SplitInfo with best feature, threshold/bitset, gain, etc. - """ - # Compute totals if not provided - if total_grad is None: - total_grad = float(_sum_histogram(hist_grad)) - if total_hess is None: - total_hess = float(_sum_histogram(hist_hess)) - - n_features = hist_grad.shape[0] - - # Check if we have categorical features - any_categorical = is_categorical is not None and np.any(is_categorical) - any_missing = has_missing is not None and np.any(has_missing) - - # If no categorical and no missing, use standard split - if not any_categorical and not any_missing: - return find_best_split( - hist_grad, hist_hess, total_grad, total_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - ) - - # If only missing (no categorical), use missing-aware split - if not any_categorical: - return find_best_split_with_missing( - hist_grad, hist_hess, total_grad, total_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - has_missing=has_missing, - ) - - # Handle mixed categorical and numeric features - # Dispatch to GPU or CPU backend - if is_cuda() and hasattr(hist_grad, '__cuda_array_interface__'): - # Phase 14.4: Use GPU kernel for categorical split finding - from .._backends._cuda import find_best_split_categorical_cuda - feature, threshold, gain, missing_left, is_cat, cat_bitset, cat_threshold = find_best_split_categorical_cuda( - hist_grad, hist_hess, - total_grad, total_hess, - reg_lambda, min_child_weight, - is_categorical if is_categorical is not None else np.zeros(n_features, dtype=np.bool_), - n_categories if n_categories is not None else np.zeros(n_features, dtype=np.int32), - has_missing if has_missing is not None else np.zeros(n_features, dtype=np.bool_), - ) - else: - hist_grad_np = np.asarray(hist_grad.copy_to_host() if hasattr(hist_grad, 'copy_to_host') else hist_grad) - hist_hess_np = np.asarray(hist_hess.copy_to_host() if hasattr(hist_hess, 'copy_to_host') else hist_hess) - - from .._backends._cpu import find_best_split_categorical_cpu - feature, threshold, gain, missing_left, is_cat, cat_bitset, cat_threshold = find_best_split_categorical_cpu( - hist_grad_np, hist_hess_np, - total_grad, total_hess, - reg_lambda, min_child_weight, - has_missing if has_missing is not None else np.zeros(n_features, dtype=np.bool_), - is_categorical if is_categorical is not None else np.zeros(n_features, dtype=np.bool_), - n_categories if n_categories is not None else np.zeros(n_features, dtype=np.int32), - ) - - # Apply minimum gain threshold - if gain < min_gain: - return SplitInfo(feature=-1, threshold=-1, gain=0.0, missing_go_left=True) - - return SplitInfo( - feature=feature, - threshold=threshold, - gain=gain, - missing_go_left=missing_left, - is_categorical=is_cat, - cat_bitset=cat_bitset, - cat_threshold=cat_threshold, - ) - diff --git a/src/openboost/_core/_tree.py b/src/openboost/_core/_tree.py deleted file mode 100644 index 14d8839..0000000 --- a/src/openboost/_core/_tree.py +++ /dev/null @@ -1,1237 +0,0 @@ -"""Tree structure and fitting for OpenBoost. - -Phase 8.3+8.4: Refactored to use growth strategies from _growth.py. -The main `fit_tree()` function now uses composable primitives and strategies. -""" - -from __future__ import annotations - -import os -import warnings -from dataclasses import dataclass, field -from typing import TYPE_CHECKING - -import numpy as np - -from .._array import BinnedArray, as_numba_array -from .._backends import is_cuda -from ._growth import ( - GrowthConfig, - GrowthStrategy, - SymmetricGrowth, - TreeStructure, - get_growth_strategy, -) - -# Legacy imports for backward compatibility with internal code -from ._histogram import build_histogram, subtract_histogram -from ._split import compute_leaf_value, find_best_split - -if TYPE_CHECKING: - from numba.cuda.cudadrv.devicearray import DeviceNDArray - from numpy.typing import NDArray - - from .._batch import ConfigBatch - from .._loss import LossFunction - - -# ============================================================================= -# Legacy Tree Classes (kept for backward compatibility) -# ============================================================================= - -@dataclass -class TreeNode: - """A node in the decision tree (legacy).""" - feature: int = -1 - threshold: int = -1 - value: float = 0.0 - left: int = -1 - right: int = -1 - n_samples: int = 0 - sum_grad: float = 0.0 - sum_hess: float = 0.0 - - @property - def is_leaf(self) -> bool: - return self.left == -1 - - -@dataclass -class Tree: - """A decision tree for gradient boosting. - - Uses array-of-structs layout for simplicity. - Can be converted to struct-of-arrays for prediction kernels. - - Supports both CPU and GPU array storage for efficient training: - - GPU arrays (_*_gpu) are used during GPU training to avoid transfers - - CPU arrays (_*) are lazily populated when needed (serialization, CPU prediction) - """ - nodes: list[TreeNode] = field(default_factory=list) - n_features: int = 0 - - # Cached CPU arrays for prediction/serialization - _features: NDArray | None = field(default=None, repr=False) - _thresholds: NDArray | None = field(default=None, repr=False) - _values: NDArray | None = field(default=None, repr=False) - _left: NDArray | None = field(default=None, repr=False) - _right: NDArray | None = field(default=None, repr=False) - - # GPU arrays for fast GPU training (Phase 5.1) - _features_gpu: DeviceNDArray | None = field(default=None, repr=False) - _thresholds_gpu: DeviceNDArray | None = field(default=None, repr=False) - _values_gpu: DeviceNDArray | None = field(default=None, repr=False) - _left_gpu: DeviceNDArray | None = field(default=None, repr=False) - _right_gpu: DeviceNDArray | None = field(default=None, repr=False) - - @property - def on_gpu(self) -> bool: - """Check if tree arrays are stored on GPU.""" - return self._features_gpu is not None - - def __len__(self) -> int: - return len(self.nodes) - - @property - def depth(self) -> int: - """Compute tree depth (number of splits from root to deepest leaf). - - A tree with just a root leaf has depth 0. - A tree with one split (root + 2 leaves) has depth 1. - """ - if not self.nodes: - return 0 - return self._node_depth(0) - - def _node_depth(self, idx: int) -> int: - node = self.nodes[idx] - if node.is_leaf: - return 0 # Leaf contributes 0 to depth - return 1 + max( - self._node_depth(node.left), - self._node_depth(node.right) - ) - - @property - def n_leaves(self) -> int: - return sum(1 for n in self.nodes if n.is_leaf) - - def to_arrays(self) -> tuple[NDArray, NDArray, NDArray, NDArray, NDArray]: - """Convert to struct-of-arrays for prediction kernels (CPU). - - If arrays are on GPU, lazily copies them to CPU. - - Returns: - features: (n_nodes,) int32 - thresholds: (n_nodes,) uint8 (leaf nodes use 0, doesn't matter) - values: (n_nodes,) float32 - left: (n_nodes,) int32 - right: (n_nodes,) int32 - """ - if self._features is not None: - return self._features, self._thresholds, self._values, self._left, self._right - - # If we have GPU arrays, copy them to CPU (lazy transfer) - if self.on_gpu: - self._features = self._features_gpu.copy_to_host() - self._thresholds = self._thresholds_gpu.copy_to_host() - self._values = self._values_gpu.copy_to_host() - self._left = self._left_gpu.copy_to_host() - self._right = self._right_gpu.copy_to_host() - return self._features, self._thresholds, self._values, self._left, self._right - - # Build from nodes (CPU path) - features = np.array([node.feature for node in self.nodes], dtype=np.int32) - # For leaf nodes (threshold=-1), use 0 since we won't use it anyway - thresholds = np.array([max(0, node.threshold) for node in self.nodes], dtype=np.uint8) - values = np.array([node.value for node in self.nodes], dtype=np.float32) - left = np.array([node.left for node in self.nodes], dtype=np.int32) - right = np.array([node.right for node in self.nodes], dtype=np.int32) - - # Cache for reuse - self._features = features - self._thresholds = thresholds - self._values = values - self._left = left - self._right = right - - return features, thresholds, values, left, right - - def __getstate__(self) -> dict: - # DeviceNDArray handles cannot be deep-copied or pickled (Cython objects - # with non-trivial __cinit__), which breaks EarlyStopping's restore_best - # snapshot and pickling of GPU-trained models. Materialize the CPU - # arrays first, then drop the device handles; they re-upload lazily. - if self.on_gpu: - self.to_arrays() - state = self.__dict__.copy() - for key in ('_features_gpu', '_thresholds_gpu', '_values_gpu', - '_left_gpu', '_right_gpu'): - state[key] = None - return state - - def __setstate__(self, state: dict) -> None: - self.__dict__.update(state) - - def to_gpu_arrays(self): - """Get GPU arrays for fast GPU prediction. - - Returns arrays already on GPU if available, otherwise transfers from CPU. - - Returns: - features_gpu, thresholds_gpu, values_gpu, left_gpu, right_gpu - """ - if self.on_gpu: - return (self._features_gpu, self._thresholds_gpu, self._values_gpu, - self._left_gpu, self._right_gpu) - - # Transfer CPU arrays to GPU - from numba import cuda - - # Ensure CPU arrays exist - self.to_arrays() - - # Transfer to GPU and cache - self._features_gpu = cuda.to_device(self._features) - self._thresholds_gpu = cuda.to_device(self._thresholds) - self._values_gpu = cuda.to_device(self._values) - self._left_gpu = cuda.to_device(self._left) - self._right_gpu = cuda.to_device(self._right) - - return (self._features_gpu, self._thresholds_gpu, self._values_gpu, - self._left_gpu, self._right_gpu) - - def __call__(self, X: BinnedArray | NDArray) -> NDArray: - """Predict using this tree. - - Args: - X: BinnedArray or binned data array (n_features, n_samples) - - Returns: - predictions: Shape (n_samples,), float32 - """ - return predict_tree(self, X) - - -# ============================================================================= -# GPU-Native Tree Building (Phase 3.2+) -# ============================================================================= - -def fit_tree_gpu_native( - X: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - *, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - min_gain: float = 0.0, - pred_gpu=None, - learning_rate: float = 0.0, - const_hess: float = 0.0, -) -> Tree: - """Fit a tree using GPU-native building (Phase 3.2). - - This is the fastest tree building method. It: - - Builds the entire tree on GPU with O(depth) kernel launches - - Has ZERO copy_to_host() during building - - Uses level-wise parallel histogram building and split finding - - Args: - X: Binned feature data (BinnedArray from ob.array(), or raw binned array) - grad: Gradient vector, shape (n_samples,), float32 - hess: Hessian vector, shape (n_samples,), float32 - max_depth: Maximum tree depth - min_child_weight: Minimum sum of hessian in a leaf - reg_lambda: L2 regularization - min_gain: Minimum gain to make a split - - Returns: - Fitted Tree object (legacy Tree, not TreeStructure). - - Note: - This intentionally returns a ``Tree`` (legacy class) rather than - ``TreeStructure`` (returned by ``fit_tree``). ``Tree`` keeps GPU - arrays directly for zero-copy training, while ``TreeStructure`` is - a struct-of-arrays representation used by the growth strategies. - If you need a ``TreeStructure``, use ``fit_tree()`` instead. - """ - if not is_cuda(): - # Fall back to CPU recursive implementation - return _fit_tree_cpu(X, grad, hess, max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, min_gain=min_gain) - - # Handle BinnedArray - if isinstance(X, BinnedArray): - binned = X.data - n_features = X.n_features - else: - binned = X - n_features = binned.shape[0] - - # Ensure data is on GPU - binned = as_numba_array(binned) - grad = as_numba_array(grad) - hess = as_numba_array(hess) - - # Build tree on GPU - from .._backends._cuda import build_tree_gpu_native - - node_features, node_thresholds, node_values, node_left, node_right = build_tree_gpu_native( - binned, grad, hess, - max_depth=max_depth, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - pred_gpu=pred_gpu, - learning_rate=learning_rate, - const_hess=const_hess, - ) - - # Phase 5.1: Keep arrays on GPU for fast training - # CPU arrays and TreeNode objects are lazily created in to_arrays() if needed - tree = Tree(n_features=n_features) - - # Store GPU arrays directly (NO copy to host!) - tree._features_gpu = node_features - tree._thresholds_gpu = node_thresholds - tree._values_gpu = node_values - tree._left_gpu = node_left - tree._right_gpu = node_right - - return tree - - -def fit_tree( - X: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - *, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, - min_gain: float = 0.0, - gamma: float | None = None, # Alias for min_gain (XGBoost compat) - growth: str | GrowthStrategy = "levelwise", - max_leaves: int | None = None, - subsample: float = 1.0, - colsample_bytree: float = 1.0, -) -> TreeStructure: - """Fit a single gradient boosting tree. - - This is the core function of OpenBoost. It builds a tree using the - specified growth strategy and returns a TreeStructure that can be - used for prediction. - - Phase 8: Uses composable growth strategies from _growth.py. - Phase 11: Added reg_alpha, subsample, colsample_bytree. - Phase 14: Handles missing values automatically via BinnedArray.has_missing. - - Args: - X: Binned feature data (BinnedArray from ob.array(), or raw binned array) - Missing values (NaN in original data) are encoded as bin 255. - grad: Gradient vector, shape (n_samples,), float32 - hess: Hessian vector, shape (n_samples,), float32 - max_depth: Maximum tree depth - min_child_weight: Minimum sum of hessian in a leaf - reg_lambda: L2 regularization on leaf values - reg_alpha: L1 regularization on leaf values (Phase 11) - min_gain: Minimum gain to make a split - gamma: Alias for min_gain (XGBoost compatibility) - growth: Growth strategy - "levelwise", "leafwise", "symmetric", - or a GrowthStrategy instance - max_leaves: Maximum leaves (for leafwise growth) - subsample: Row sampling ratio (0.0-1.0), 1.0 = no sampling (Phase 11) - colsample_bytree: Column sampling ratio (0.0-1.0), 1.0 = no sampling (Phase 11) - - Returns: - TreeStructure that can predict via tree.predict(X) or tree(X) - - Example: - >>> import openboost as ob - >>> import numpy as np - >>> - >>> # Missing values handled automatically - >>> X_train = np.array([[1.0, np.nan], [2.0, 3.0], [np.nan, 4.0]]) - >>> X_binned = ob.array(X_train) - >>> pred = np.zeros(3, dtype=np.float32) - >>> - >>> for round in range(100): - ... grad = 2 * (pred - y) # MSE gradient - ... hess = np.ones_like(grad) * 2 - ... tree = ob.fit_tree(X_binned, grad, hess) - ... pred = pred + 0.1 * tree.predict(X_binned) - - >>> # Use leaf-wise growth (LightGBM style) - >>> tree = ob.fit_tree(X_binned, grad, hess, growth="leafwise", max_leaves=32) - - >>> # Use symmetric growth (CatBoost style) - >>> tree = ob.fit_tree(X_binned, grad, hess, growth="symmetric") - - >>> # Stochastic gradient boosting (Phase 11) - >>> tree = ob.fit_tree(X_binned, grad, hess, subsample=0.8, colsample_bytree=0.8) - """ - # Handle gamma alias - if gamma is not None: - min_gain = gamma - - # Extract binned data and missing/categorical info - has_missing = None - is_categorical = None - n_categories = None - if isinstance(X, BinnedArray): - binned = X.data - n_features = X.n_features - n_samples = X.n_samples - # Phase 14: Get missing value info if available - if hasattr(X, 'has_missing') and len(X.has_missing) > 0: - has_missing = X.has_missing - # Phase 14.3: Get categorical info if available - if hasattr(X, 'is_categorical') and len(X.is_categorical) > 0: - is_categorical = X.is_categorical - if hasattr(X, 'n_categories') and len(X.n_categories) > 0: - n_categories = X.n_categories - else: - binned = X - n_features, n_samples = binned.shape - - # Convert grad/hess to appropriate format - grad = as_numba_array(grad) - hess = as_numba_array(hess) - - # Validate shapes - if grad.shape[0] != n_samples: - raise ValueError(f"grad has {grad.shape[0]} samples, expected {n_samples}") - if hess.shape[0] != n_samples: - raise ValueError(f"hess has {hess.shape[0]} samples, expected {n_samples}") - - # Apply row subsampling (Phase 11) - if subsample < 1.0: - n_subsample = int(n_samples * subsample) - if n_subsample < 1: - n_subsample = 1 - subsample_indices = np.random.choice(n_samples, n_subsample, replace=False) - subsample_indices = np.sort(subsample_indices) # Keep order for cache efficiency - # Create mask for sampling - subsample_mask = np.zeros(n_samples, dtype=np.bool_) - subsample_mask[subsample_indices] = True - else: - subsample_mask = None - - # Get growth strategy - strategy = get_growth_strategy(growth) if isinstance(growth, str) else growth - - # Build config - config = GrowthConfig( - max_depth=max_depth, - max_leaves=max_leaves, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - reg_alpha=reg_alpha, - min_gain=min_gain, - subsample=subsample, - colsample_bytree=colsample_bytree, - ) - - # Apply subsampling to gradients if needed - if subsample_mask is not None: - # Zero out gradients for non-sampled rows. - # Design choice: we zero the grad/hess for non-sampled rows rather than - # physically removing them. This is correct (zero-weight samples don't - # affect the Newton step) but doesn't provide the performance benefit of - # true subsampling since histogram building still iterates over all samples. - # TODO: For a performance improvement, consider physically filtering to - # only the sampled rows before histogram building. - # Handle both CPU (numpy) and GPU (DeviceNDArray) arrays - if hasattr(grad, '__cuda_array_interface__'): - # GPU path: copy to host, modify, copy back - from numba import cuda - grad_host = grad.copy_to_host() - hess_host = hess.copy_to_host() - grad_host[~subsample_mask] = 0.0 - hess_host[~subsample_mask] = 0.0 - grad_sampled = cuda.to_device(grad_host) - hess_sampled = cuda.to_device(hess_host) - else: - # CPU path - grad_sampled = grad.copy() - hess_sampled = hess.copy() - grad_sampled[~subsample_mask] = 0.0 - hess_sampled[~subsample_mask] = 0.0 - # Phase 14/14.3: Pass has_missing and categorical info to growth strategy - return strategy.grow( - binned, grad_sampled, hess_sampled, config, - has_missing=has_missing, - is_categorical=is_categorical, - n_categories=n_categories, - ) - else: - # Phase 14/14.3: Pass has_missing and categorical info to growth strategy - return strategy.grow( - binned, grad, hess, config, - has_missing=has_missing, - is_categorical=is_categorical, - n_categories=n_categories, - ) - - -def fit_tree_legacy( - X: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - *, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - min_gain: float = 0.0, -) -> Tree: - """Legacy fit_tree that returns the old Tree class. - - Kept for backward compatibility with code that depends on Tree internals. - For new code, use fit_tree() which returns TreeStructure. - """ - # Extract binned data - if isinstance(X, BinnedArray): - binned = X.data - n_features = X.n_features - n_samples = X.n_samples - else: - binned = X - n_features, n_samples = binned.shape - - # Convert grad/hess to appropriate format - grad = as_numba_array(grad) - hess = as_numba_array(hess) - - # Validate shapes - if grad.shape[0] != n_samples: - raise ValueError(f"grad has {grad.shape[0]} samples, expected {n_samples}") - if hess.shape[0] != n_samples: - raise ValueError(f"hess has {hess.shape[0]} samples, expected {n_samples}") - - # Phase 4: Auto-dispatch to GPU-native when data is on GPU - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - return fit_tree_gpu_native( - X, grad, hess, - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - min_gain=min_gain, - ) - - # CPU path: use recursive implementation - tree = Tree(n_features=n_features) - sample_indices = np.arange(n_samples, dtype=np.int32) - - _build_tree_recursive( - tree=tree, - binned=binned, - grad=grad, - hess=hess, - sample_indices=sample_indices, - depth=0, - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - min_gain=min_gain, - ) - - return tree - - -def _fit_tree_cpu( - X: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - *, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - min_gain: float = 0.0, -) -> Tree: - """CPU-only tree fitting using recursive implementation. - - This is the fallback when GPU is not available. - Phase 4: Extracted from fit_tree for clarity. - """ - # Extract binned data - if isinstance(X, BinnedArray): - binned = X.data - n_features = X.n_features - n_samples = X.n_samples - else: - binned = X - n_features, n_samples = binned.shape - - # Ensure CPU arrays - binned = np.asarray(binned) - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - - tree = Tree(n_features=n_features) - sample_indices = np.arange(n_samples, dtype=np.int32) - - _build_tree_recursive( - tree=tree, - binned=binned, - grad=grad, - hess=hess, - sample_indices=sample_indices, - depth=0, - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - min_gain=min_gain, - ) - - return tree - - -def _build_tree_recursive( - tree: Tree, - binned: NDArray, - grad: NDArray, - hess: NDArray, - sample_indices: NDArray, - depth: int, - max_depth: int, - min_child_weight: float, - reg_lambda: float, - min_gain: float, - parent_hist_grad: NDArray | None = None, - parent_hist_hess: NDArray | None = None, - sibling_hist_grad: NDArray | None = None, - sibling_hist_hess: NDArray | None = None, -) -> int: - """Recursively build tree nodes. - - Returns the index of the created node. - - Phase 3: Uses histogram subtraction for ~2x faster histogram building. - - If sibling_hist provided: compute this node's histogram via subtraction - - Otherwise: build histogram directly - - Pass histogram to children for subtraction trick - """ - n_samples = sample_indices.shape[0] - - # Early exit for trivial cases (before building histogram) - if depth >= max_depth or n_samples < 2: - # Need to compute sums for leaf value - if sibling_hist_grad is not None and parent_hist_grad is not None: - # Use subtraction to get sums - hist_grad, hist_hess = subtract_histogram( - parent_hist_grad, parent_hist_hess, - sibling_hist_grad, sibling_hist_hess - ) - if is_cuda() and hasattr(hist_grad, '__cuda_array_interface__'): - sum_grad = float(np.sum(hist_grad[0].copy_to_host())) - sum_hess = float(np.sum(hist_hess[0].copy_to_host())) - else: - sum_grad = float(np.sum(hist_grad[0])) - sum_hess = float(np.sum(hist_hess[0])) - elif is_cuda() and hasattr(grad, '__cuda_array_interface__'): - from .._backends._cuda import reduce_sum_indexed_cuda - sum_grad = float(reduce_sum_indexed_cuda(grad, sample_indices).copy_to_host()[0]) - sum_hess = float(reduce_sum_indexed_cuda(hess, sample_indices).copy_to_host()[0]) - else: - sample_indices_cpu = np.asarray(sample_indices) - sum_grad = float(np.sum(grad[sample_indices_cpu])) - sum_hess = float(np.sum(hess[sample_indices_cpu])) - - node_idx = len(tree.nodes) - node = TreeNode(n_samples=n_samples, sum_grad=sum_grad, sum_hess=sum_hess) - node.value = compute_leaf_value(sum_grad, sum_hess, reg_lambda) - tree.nodes.append(node) - return node_idx - - # Build or compute histogram - if sibling_hist_grad is not None and parent_hist_grad is not None: - # Phase 3: Use subtraction trick (O(n_features * 256) instead of O(n_features * n_samples)) - hist_grad, hist_hess = subtract_histogram( - parent_hist_grad, parent_hist_hess, - sibling_hist_grad, sibling_hist_hess - ) - else: - # Build histogram directly (root node, or fallback) - hist_grad, hist_hess = build_histogram(binned, grad, hess, sample_indices) - - # Get sum_grad/sum_hess from histogram (sum across all bins for any feature) - if is_cuda() and hasattr(hist_grad, '__cuda_array_interface__'): - hist_grad_cpu = hist_grad[0].copy_to_host() # Shape (256,) - hist_hess_cpu = hist_hess[0].copy_to_host() # Shape (256,) - sum_grad = float(np.sum(hist_grad_cpu)) - sum_hess = float(np.sum(hist_hess_cpu)) - else: - sum_grad = float(np.sum(hist_grad[0])) - sum_hess = float(np.sum(hist_hess[0])) - - # Create node - node_idx = len(tree.nodes) - node = TreeNode( - n_samples=n_samples, - sum_grad=sum_grad, - sum_hess=sum_hess, - ) - tree.nodes.append(node) - - # Check min_child_weight stopping condition - if sum_hess < min_child_weight: - node.value = compute_leaf_value(sum_grad, sum_hess, reg_lambda) - return node_idx - - # Find best split - split = find_best_split( - hist_grad, hist_hess, - sum_grad, sum_hess, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - ) - - if not split.is_valid: - # No valid split, make leaf - node.value = compute_leaf_value(sum_grad, sum_hess, reg_lambda) - return node_idx - - # Split samples - if is_cuda() and hasattr(binned, '__cuda_array_interface__'): - from .._backends._cuda import partition_samples_cuda - - left_indices, right_indices, n_left, n_right = partition_samples_cuda( - binned, sample_indices, split.feature, split.threshold - ) - - if n_left == 0 or n_right == 0: - node.value = compute_leaf_value(sum_grad, sum_hess, reg_lambda) - return node_idx - else: - binned_cpu = np.asarray(binned) - sample_indices_cpu = np.asarray(sample_indices) - - feature_values = binned_cpu[split.feature, sample_indices_cpu] - left_mask = feature_values <= split.threshold - - left_indices = sample_indices_cpu[left_mask].astype(np.int32) - right_indices = sample_indices_cpu[~left_mask].astype(np.int32) - n_left = len(left_indices) - n_right = len(right_indices) - - if n_left == 0 or n_right == 0: - node.value = compute_leaf_value(sum_grad, sum_hess, reg_lambda) - return node_idx - - # Set split info - node.feature = split.feature - node.threshold = split.threshold - - # Phase 3: Histogram subtraction - build only smaller child, subtract for larger - # This gives ~2x speedup on histogram building - if n_left <= n_right: - # Build left (smaller), subtract for right - left_hist_grad, left_hist_hess = build_histogram(binned, grad, hess, left_indices) - - left_idx = _build_tree_recursive( - tree, binned, grad, hess, left_indices, - depth + 1, max_depth, min_child_weight, reg_lambda, min_gain, - parent_hist_grad=hist_grad, parent_hist_hess=hist_hess, - sibling_hist_grad=None, sibling_hist_hess=None, # Already built - ) - # Store left histogram in node for right child's subtraction - right_idx = _build_tree_recursive( - tree, binned, grad, hess, right_indices, - depth + 1, max_depth, min_child_weight, reg_lambda, min_gain, - parent_hist_grad=hist_grad, parent_hist_hess=hist_hess, - sibling_hist_grad=left_hist_grad, sibling_hist_hess=left_hist_hess, - ) - else: - # Build right (smaller), subtract for left - right_hist_grad, right_hist_hess = build_histogram(binned, grad, hess, right_indices) - - left_idx = _build_tree_recursive( - tree, binned, grad, hess, left_indices, - depth + 1, max_depth, min_child_weight, reg_lambda, min_gain, - parent_hist_grad=hist_grad, parent_hist_hess=hist_hess, - sibling_hist_grad=right_hist_grad, sibling_hist_hess=right_hist_hess, - ) - right_idx = _build_tree_recursive( - tree, binned, grad, hess, right_indices, - depth + 1, max_depth, min_child_weight, reg_lambda, min_gain, - parent_hist_grad=hist_grad, parent_hist_hess=hist_hess, - sibling_hist_grad=None, sibling_hist_hess=None, # Already built - ) - - # Update node with children indices - tree.nodes[node_idx].left = left_idx - tree.nodes[node_idx].right = right_idx - - return node_idx - - -def predict_tree(tree: Tree, X: BinnedArray | NDArray) -> NDArray: - """Predict using a fitted tree. - - Args: - tree: Fitted Tree object - X: BinnedArray or binned data (n_features, n_samples) - - Returns: - predictions: Shape (n_samples,), float32 - """ - # Get binned data - if isinstance(X, BinnedArray): - binned = X.data - device = X.device - else: - binned = X - device = "cuda" if is_cuda() and hasattr(binned, '__cuda_array_interface__') else "cpu" - - # Convert tree to arrays - features, thresholds, values, left, right = tree.to_arrays() - - # Dispatch to backend - if device == "cuda" and is_cuda(): - from .._backends._cuda import predict_cuda, to_device - return predict_cuda( - binned, - to_device(features), - to_device(thresholds), - to_device(values), - to_device(left), - to_device(right), - ) - else: - from .._backends._cpu import predict_cpu - binned_cpu = binned.copy_to_host() if hasattr(binned, 'copy_to_host') else np.asarray(binned) - return predict_cpu(binned_cpu, features, thresholds, values, left, right) - - -# ============================================================================= -# Batch Training (Phase 2 P2) -# ============================================================================= - -def fit_trees_batch( - X: BinnedArray | NDArray, - grad: NDArray | None = None, - hess: NDArray | None = None, - configs: ConfigBatch | None = None, - *, - y: NDArray | None = None, - loss: str | LossFunction = "mse", - min_gain: float = 0.0, -) -> list[list[Tree]]: - """Fit multiple boosted tree ensembles while sharing binned input data. - - This function is the correctness reference for train-many optimization. - Configurations currently train sequentially, but expensive input binning is - shared. Future GPU fusion can optimize this path without changing its results. - - Args: - X: Binned feature data (BinnedArray from ob.array()) - grad: Initial gradient vector for the legacy one-round API. - hess: Initial hessian vector for the legacy one-round API. - configs: ConfigBatch with hyperparameter configurations - y: Training targets. Required when ``n_rounds`` is greater than one. - loss: Built-in loss name or gradient/hessian callable. - min_gain: Minimum gain to make a split - - Returns: - List of tree lists, one per configuration. - ``trees[config_idx][round_idx]`` gives the corresponding round's tree. - - Example: - >>> import openboost as ob - >>> - >>> # Create hyperparameter grid - >>> configs = ob.ConfigBatch.from_grid( - ... max_depth=[4, 6, 8], - ... reg_lambda=[0.1, 1.0, 10.0], - ... learning_rate=[0.1], - ... n_rounds=100, - ... ) - >>> - >>> # Bin data once - >>> X_binned = ob.array(X_train) - >>> - >>> # Train all configs against the same target - >>> all_trees = ob.fit_trees_batch(X_binned, configs=configs, y=y_train) - >>> - >>> # all_trees[0] contains trees for first config, etc. - """ - from .._batch import BatchTrainingState, ConfigBatch - from .._loss import get_loss_function - - if not isinstance(configs, ConfigBatch): - raise TypeError(f"configs must be ConfigBatch, got {type(configs)}") - - # Extract binned data - if isinstance(X, BinnedArray): - binned = X.data - n_samples = X.n_samples - else: - binned = X - _, n_samples = binned.shape - - if y is None: - if grad is None or hess is None: - raise ValueError("Provide y for train-many fitting, or grad and hess for one round") - if configs.n_rounds != 1: - raise ValueError("y is required when configs.n_rounds is greater than one") - initial_grad = as_numba_array(grad) - initial_hess = as_numba_array(hess) - loss_fn = None - else: - if hasattr(y, "copy_to_host"): - y = y.copy_to_host() - y = np.asarray(y, dtype=np.float32) - if y.ndim != 1 or len(y) != n_samples: - raise ValueError(f"y must have shape ({n_samples},), got {y.shape}") - initial_grad = None - initial_hess = None - loss_fn = get_loss_function(loss) - - # Initialize training state - state = BatchTrainingState.create(configs.n_configs, n_samples) - return _fit_trees_batch_reference( - binned, - configs, - state, - min_gain, - n_samples, - y=y, - loss_fn=loss_fn, - initial_grad=initial_grad, - initial_hess=initial_hess, - ) - - -def _fit_trees_batch_reference( - binned: NDArray, - configs, - state, - min_gain: float, - n_samples: int, - *, - y: NDArray | None, - loss_fn, - initial_grad: NDArray | None, - initial_hess: NDArray | None, -) -> list[list[Tree]]: - """Correct sequential reference used by CPU and CUDA tree builders.""" - n_configs = configs.n_configs - n_rounds = configs.n_rounds - - # Train each config sequentially - for config_idx in range(n_configs): - config = configs[config_idx] - pred = np.zeros(n_samples, dtype=np.float32) - - for _round_idx in range(n_rounds): - if y is None: - round_grad, round_hess = initial_grad, initial_hess - else: - round_grad, round_hess = loss_fn(pred, y) - round_grad = as_numba_array(round_grad) - round_hess = as_numba_array(round_hess) - - tree = fit_tree( - binned, - round_grad, - round_hess, - max_depth=config['max_depth'], - min_child_weight=config['min_child_weight'], - reg_lambda=config['reg_lambda'], - min_gain=min_gain, - ) - state.trees[config_idx].append(tree) - - # Update predictions - tree_pred = tree(binned) - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred = pred + config['learning_rate'] * tree_pred - state.predictions[config_idx] = pred - - return state.trees - - -# ============================================================================= -# Phase 3.4: Symmetric (Oblivious) Trees -# ============================================================================= - -@dataclass -class SymmetricTree: - """A symmetric (oblivious) decision tree. - - All nodes at the same depth use the SAME split (feature + threshold). - This enables massive GPU parallelization. - - Structure: - - level_features[d]: Feature used at depth d - - level_thresholds[d]: Threshold used at depth d - - leaf_values[i]: Value for leaf i (2^max_depth leaves) - - Prediction: - leaf_idx = 0 - for d in range(max_depth): - if X[level_features[d]] > level_thresholds[d]: - leaf_idx = 2 * leaf_idx + 1 - else: - leaf_idx = 2 * leaf_idx - return leaf_values[leaf_idx] - """ - level_features: NDArray # (max_depth,) int32 - feature at each level - level_thresholds: NDArray # (max_depth,) uint8 - threshold at each level - leaf_values: NDArray # (2^max_depth,) float32 - leaf values - max_depth: int - n_features: int - - # Cached GPU arrays - _level_features_gpu: NDArray | None = field(default=None, repr=False) - _level_thresholds_gpu: NDArray | None = field(default=None, repr=False) - _leaf_values_gpu: NDArray | None = field(default=None, repr=False) - - def __getstate__(self) -> dict: - # Device handles cannot be deep-copied/pickled; host arrays are the - # source of truth, caches re-upload lazily (see Tree.__getstate__). - state = self.__dict__.copy() - for key in ('_level_features_gpu', '_level_thresholds_gpu', '_leaf_values_gpu'): - state[key] = None - return state - - def __setstate__(self, state: dict) -> None: - self.__dict__.update(state) - - def __call__(self, X: BinnedArray | NDArray) -> NDArray: - """Predict using this symmetric tree.""" - return predict_symmetric_tree(self, X) - - @property - def n_leaves(self) -> int: - return 2 ** self.max_depth - - -def fit_tree_symmetric( - binned: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - min_gain: float = 0.0, -) -> SymmetricTree: - """Fit a symmetric (oblivious) tree. - - All nodes at the same depth use the same split, chosen by aggregating - per-leaf gains across all leaves at that depth (CatBoost-style). - Delegates to ``SymmetricGrowth`` so this entry point and - ``fit_tree(..., growth='symmetric')`` share one implementation. - - Args: - binned: BinnedArray or binned data (n_features, n_samples) - grad: Gradients, shape (n_samples,) - hess: Hessians, shape (n_samples,) - max_depth: Maximum tree depth - min_child_weight: Minimum sum of hessian in child - reg_lambda: L2 regularization - min_gain: Minimum gain to make a split - - Returns: - SymmetricTree with level-wise splits - """ - # Extract raw data - if isinstance(binned, BinnedArray): - binned_data = binned.data - n_features = binned.n_features - else: - binned_data = binned - n_features = binned.shape[0] - - config = GrowthConfig( - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - min_gain=min_gain, - ) - # No categorical info here: this API treats all features as ordinal, - # which keeps SymmetricTree's ordinal-only prediction consistent. - ts = SymmetricGrowth().grow(binned_data, grad, hess, config) - - n_leaves = 2 ** max_depth - level_features = np.full(max_depth, -1, dtype=np.int32) - level_thresholds = np.zeros(max_depth, dtype=np.uint8) - leaf_values = np.zeros(n_leaves, dtype=np.float32) - - d = ts.depth - if d > 0: - level_features[:d] = ts.level_features - level_thresholds[:d] = ts.level_thresholds.astype(np.uint8) - # SymmetricTree prediction stops at the first feature < 0, so leaf ids - # stay in [0, 2^d); map the TreeStructure leaf slots onto that range. - leaf_start = 2 ** d - 1 - leaf_values[:2 ** d] = ts.leaf_values_array[leaf_start:leaf_start + 2 ** d] - - return SymmetricTree( - level_features=level_features, - level_thresholds=level_thresholds, - leaf_values=leaf_values, - max_depth=max_depth, - n_features=n_features, - ) - - -def predict_symmetric_tree(tree: SymmetricTree, X: BinnedArray | NDArray) -> NDArray: - """Predict using a symmetric tree. - - Prediction is just bit operations - very fast! - """ - binned = X.data if isinstance(X, BinnedArray) else X - - use_gpu = is_cuda() and hasattr(binned, '__cuda_array_interface__') - - if use_gpu: - from .._backends._cuda import predict_symmetric_cuda - return predict_symmetric_cuda( - binned, - tree.level_features, - tree.level_thresholds, - tree.leaf_values, - tree.max_depth, - ) - else: - return _predict_symmetric_cpu( - np.asarray(binned), - tree.level_features, - tree.level_thresholds, - tree.leaf_values, - tree.max_depth, - ) - - -def _predict_symmetric_cpu( - binned: NDArray, - level_features: NDArray, - level_thresholds: NDArray, - leaf_values: NDArray, - max_depth: int, -) -> NDArray: - """CPU prediction for symmetric trees.""" - n_samples = binned.shape[1] - leaf_ids = np.zeros(n_samples, dtype=np.int32) - - for depth in range(max_depth): - feature = level_features[depth] - if feature < 0: - break - threshold = level_thresholds[depth] - goes_right = binned[feature, :] > threshold - leaf_ids = 2 * leaf_ids + goes_right.astype(np.int32) - - return leaf_values[leaf_ids] - - -def fit_tree_symmetric_gpu_native( - binned: BinnedArray | NDArray, - grad: NDArray, - hess: NDArray, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - min_gain: float = 0.0, -) -> SymmetricTree: - """Fit symmetric tree using GPU-native implementation. - - Faster than fit_tree_symmetric() as it minimizes CPU-GPU transfers. - - Warning: - The GPU kernel is currently DISABLED pending a correctness fix: - ``_build_symmetric_histogram_kernel`` ignores per-leaf sample - assignments, so every depth rebuilds the identical root histogram - and the tree repeats one split per level (degenerate). Unless - ``OPENBOOST_EXPERIMENTAL_SYMMETRIC_GPU=1`` is set, this function - falls back to the correct (CPU-side) symmetric builder. - - Args: - binned: BinnedArray or binned data (n_features, n_samples) - grad: Gradients, shape (n_samples,) - hess: Hessians, shape (n_samples,) - max_depth: Maximum tree depth - min_child_weight: Minimum sum of hessian in child - reg_lambda: L2 regularization - min_gain: Minimum gain to make a split - - Returns: - SymmetricTree - """ - if not is_cuda(): - return fit_tree_symmetric( - binned, grad, hess, - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - min_gain=min_gain, - ) - - # CRIT-5 gate: the GPU symmetric builder produces degenerate trees because - # its histogram kernel is not leaf-aware (see _backends/_cuda.py, - # _build_symmetric_histogram_kernel). Route to the correct CPU builder - # unless the experimental escape hatch is explicitly enabled. - if os.environ.get("OPENBOOST_EXPERIMENTAL_SYMMETRIC_GPU") != "1": - warnings.warn( - "symmetric GPU builder disabled pending correctness fix: its " - "histogram kernel ignores per-leaf sample assignments, so every " - "depth repeats the same split. Using the CPU symmetric builder " - "instead. Set OPENBOOST_EXPERIMENTAL_SYMMETRIC_GPU=1 to force " - "the (known-broken) GPU kernel.", - UserWarning, - stacklevel=2, - ) - return fit_tree_symmetric( - binned, grad, hess, - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - min_gain=min_gain, - ) - - - # Extract raw data - if isinstance(binned, BinnedArray): - binned_data = binned.data - n_features = binned.n_features - else: - binned_data = binned - n_features = binned.shape[0] - - # Ensure data is on GPU - from numba import cuda - if not hasattr(binned_data, '__cuda_array_interface__'): - binned_data = cuda.to_device(np.ascontiguousarray(binned_data)) - if not hasattr(grad, '__cuda_array_interface__'): - grad = cuda.to_device(np.ascontiguousarray(grad, dtype=np.float32)) - if not hasattr(hess, '__cuda_array_interface__'): - hess = cuda.to_device(np.ascontiguousarray(hess, dtype=np.float32)) - - from .._backends._cuda import build_tree_symmetric_gpu_native - - level_features_gpu, level_thresholds_gpu, leaf_values_gpu = build_tree_symmetric_gpu_native( - binned_data, grad, hess, - max_depth=max_depth, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain, - ) - - # Copy results to CPU for tree structure - level_features = level_features_gpu.copy_to_host().astype(np.int32) - level_thresholds = level_thresholds_gpu.copy_to_host().astype(np.uint8) - leaf_values = leaf_values_gpu.copy_to_host().astype(np.float32) - - return SymmetricTree( - level_features=level_features, - level_thresholds=level_thresholds, - leaf_values=leaf_values, - max_depth=max_depth, - n_features=n_features, - ) diff --git a/src/openboost/_distributed/__init__.py b/src/openboost/_distributed/__init__.py deleted file mode 100644 index 0901ef6..0000000 --- a/src/openboost/_distributed/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Distributed training for OpenBoost. - -Phase 12: Adds Ray-based distributed training capability. -Phase 18: Adds multi-GPU support via Ray actors. -""" - -from typing import Any, Protocol - -from numpy.typing import NDArray - - -class DistributedContext(Protocol): - """Protocol for distributed training context.""" - n_workers: int - rank: int - - def allreduce_histograms(self, local_hist: NDArray) -> NDArray: - """Sum histograms across all workers.""" - ... - - def broadcast_tree(self, tree: Any) -> Any: - """Broadcast tree from rank 0 to all workers.""" - ... - - def partition_data(self, X: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """Get this worker's data shard.""" - ... - - -# Phase 18: Multi-GPU support -from ._multigpu import ( - GPUWorker, - GPUWorkerBase, - MultiGPUContext, - fit_tree_multigpu, -) - -__all__ = [ - "DistributedContext", - # Phase 18: Multi-GPU - "GPUWorkerBase", - "GPUWorker", - "MultiGPUContext", - "fit_tree_multigpu", -] diff --git a/src/openboost/_distributed/_multigpu.py b/src/openboost/_distributed/_multigpu.py deleted file mode 100644 index d8ed219..0000000 --- a/src/openboost/_distributed/_multigpu.py +++ /dev/null @@ -1,785 +0,0 @@ -"""Multi-GPU training support for OpenBoost using Ray. - -Phase 18: Implements data-parallel multi-GPU training where each GPU holds -a subset of the data and computes local histograms, which are then aggregated. - -Architecture: - GPU 0: samples 0-N/4 → local histograms → ┐ - GPU 1: samples N/4-N/2 → local histograms → ├→ AllReduce → global histograms - GPU 2: samples N/2-3N/4 → local histograms → │ - GPU 3: samples 3N/4-N → local histograms → ┘ - -Usage: - # Simple API - model = ob.GradientBoosting(n_trees=100, n_gpus=4) - model.fit(X, y) - - # Or explicit device list - model = ob.GradientBoosting(n_trees=100, devices=[0, 1, 2, 3]) - model.fit(X, y) -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any - -import numpy as np -from numpy.typing import NDArray - -try: - import ray -except ImportError: - ray = None - -if TYPE_CHECKING: - from .._core._growth import TreeStructure - from .._loss import LossFunction - - -# ============================================================================= -# GPUWorker: Ray actor that owns one GPU and a data shard -# ============================================================================= - -class GPUWorkerBase: - """Ray actor that owns one GPU and holds a shard of the training data. - - Each worker: - 1. Holds a subset of samples (data shard) - 2. Computes local gradients on its GPU - 3. Builds local histograms for tree construction - 4. Updates local predictions with new trees - - Note: This class is decorated with @ray.remote(num_gpus=1) when Ray is available. - """ - - def __init__( - self, - gpu_id: int, - X_shard: NDArray, - y_shard: NDArray, - n_bins: int, - bin_edges: NDArray | None = None, - ): - """Initialize worker with data shard on assigned GPU. - - Args: - gpu_id: GPU device ID to use - X_shard: Feature data shard, shape (n_samples_shard, n_features) - y_shard: Target data shard, shape (n_samples_shard,) - n_bins: Number of bins for histogram building - bin_edges: Optional pre-computed bin edges for consistent binning - Shape (n_features, n_bins + 1) - """ - try: - from numba import cuda - cuda.select_device(gpu_id) - self.has_cuda = True - except Exception: - self.has_cuda = False - - self.gpu_id = gpu_id - self.n_bins = n_bins - self.n_samples = len(y_shard) - - # Import array function - import openboost as ob - - # Bin and store data - # If bin_edges provided, use them for consistent binning across shards - if bin_edges is not None: - from .._array import BinnedArray - # Build a template BinnedArray from pre-computed edges, then transform - n_features = len(bin_edges) - template = BinnedArray( - data=np.empty(0, dtype=np.uint8), - bin_edges=bin_edges, - n_features=n_features, - n_samples=0, - device="cpu", - ) - self.X_binned = template.transform(X_shard) - else: - self.X_binned = ob.array(X_shard, n_bins=n_bins) - - self.y = y_shard.astype(np.float32) - self.n_features = self.X_binned.n_features - - # Initialize predictions (on GPU if available) - if self.has_cuda: - from numba import cuda - self.pred = cuda.device_array(self.n_samples, dtype=np.float32) - # Zero initialize - self._zero_predictions() - self.y_gpu = cuda.to_device(self.y) - else: - self.pred = np.zeros(self.n_samples, dtype=np.float32) - self.y_gpu = self.y - - # Track sample node IDs for distributed tree building - self.sample_node_ids = np.zeros(self.n_samples, dtype=np.int32) - - def _zero_predictions(self): - """Zero out predictions array on GPU.""" - if self.has_cuda: - from numba import cuda - - @cuda.jit - def _fill_zeros(arr, n): - idx = cuda.grid(1) - if idx < n: - arr[idx] = 0.0 - - threads = 256 - blocks = (self.n_samples + threads - 1) // threads - _fill_zeros[blocks, threads](self.pred, self.n_samples) - - def compute_gradients(self, loss_fn: LossFunction) -> tuple[NDArray, NDArray]: - """Compute local gradients on this GPU shard. - - Args: - loss_fn: Loss function that computes (grad, hess) from (pred, y) - - Returns: - Tuple of (gradients, hessians) arrays, shape (n_samples_shard,) - """ - if self.has_cuda and hasattr(self.pred, 'copy_to_host'): - # Try GPU-native gradient computation first - try: - grad, hess = loss_fn(self.pred, self.y_gpu) - # Return as CPU arrays for aggregation - if hasattr(grad, 'copy_to_host'): - return grad.copy_to_host().astype(np.float32), hess.copy_to_host().astype(np.float32) - return grad.astype(np.float32), hess.astype(np.float32) - except Exception: - # Fall back to CPU computation - pred_cpu = self.pred.copy_to_host() - grad, hess = loss_fn(pred_cpu, self.y) - return grad.astype(np.float32), hess.astype(np.float32) - else: - grad, hess = loss_fn(self.pred, self.y) - return grad.astype(np.float32), hess.astype(np.float32) - - def build_histogram( - self, - grad: NDArray, - hess: NDArray, - node_ids: list[int] | None = None, - ) -> tuple[NDArray, NDArray]: - """Build local histogram for this shard. - - Args: - grad: Gradient array, shape (n_samples_shard,) - hess: Hessian array, shape (n_samples_shard,) - node_ids: Optional list of node IDs to build histograms for. - If None, builds histogram for all samples (root node). - - Returns: - Tuple of (hist_grad, hist_hess) arrays - Shape: (n_features, n_bins) if node_ids is None - or dict mapping node_id to histogram - """ - from .._core._histogram import build_histogram - - # Get binned data - binned = self.X_binned.data - - # Prepare sample indices - sample_indices = np.arange(self.n_samples, dtype=np.int32) - - if self.has_cuda: - from numba import cuda - grad_gpu = cuda.to_device(grad) - hess_gpu = cuda.to_device(hess) - sample_indices_gpu = cuda.to_device(sample_indices) - - hist_grad, hist_hess = build_histogram(binned, grad_gpu, hess_gpu, sample_indices_gpu) - - # Return as CPU arrays - if hasattr(hist_grad, 'copy_to_host'): - return hist_grad.copy_to_host(), hist_hess.copy_to_host() - else: - hist_grad, hist_hess = build_histogram(binned, grad, hess, sample_indices) - - return hist_grad, hist_hess - - def update_predictions(self, tree: TreeStructure, learning_rate: float): - """Update local predictions with new tree. - - Args: - tree: Fitted tree structure - learning_rate: Learning rate to apply - """ - # Get tree predictions for this shard - tree_pred = tree(self.X_binned) - - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - - if self.has_cuda and hasattr(self.pred, 'copy_to_host'): - # Update on GPU - pred_cpu = self.pred.copy_to_host() - pred_cpu += learning_rate * tree_pred - from numba import cuda - cuda.to_device(pred_cpu, to=self.pred) - else: - self.pred += learning_rate * tree_pred - - def get_predictions(self) -> NDArray: - """Get current predictions (copies from GPU if needed).""" - if hasattr(self.pred, 'copy_to_host'): - return self.pred.copy_to_host() - return self.pred.copy() - - def get_n_features(self) -> int: - """Get number of features.""" - return self.n_features - - def get_n_samples(self) -> int: - """Get number of samples in this shard.""" - return self.n_samples - - def get_bin_edges(self) -> NDArray | None: - """Get bin edges used by this worker (for consistent binning).""" - if hasattr(self.X_binned, 'bin_edges'): - return self.X_binned.bin_edges - return None - - -# Create Ray remote version if Ray is available -GPUWorker = ray.remote(num_gpus=1)(GPUWorkerBase) if ray is not None else GPUWorkerBase - - -# ============================================================================= -# MultiGPUContext: Manages multiple GPU workers -# ============================================================================= - -@dataclass -class MultiGPUContext: - """Context manager for multi-GPU training using Ray. - - Handles: - - GPU worker creation and management - - Data sharding across GPUs - - Histogram aggregation (AllReduce) - - Tree broadcasting to workers - - Example: - ctx = MultiGPUContext(n_gpus=4) - ctx.setup(X, y, n_bins=256) - - for round in range(n_trees): - # Compute gradients on each GPU - grad_hess_refs = [w.compute_gradients.remote(loss_fn) for w in ctx.workers] - - # Build and aggregate histograms - hist_refs = [ - w.build_histogram.remote(g, h) - for w, (g, h) in zip(ctx.workers, grads) - ] - global_hist = ctx.aggregate_histograms(hist_refs) - - # Build tree from global histogram - tree = build_tree_from_histogram(global_hist, ...) - - # Update predictions on all workers - ctx.update_predictions(tree, learning_rate) - """ - - n_gpus: int = None - devices: list[int] = None - workers: list[Any] = None - n_features: int = None - n_samples: int = None - shard_sizes: list[int] = None - bin_edges: NDArray = None - - def __post_init__(self): - """Initialize Ray and detect available GPUs.""" - if ray is None: - raise ImportError( - "Multi-GPU training requires Ray. " - "Install with: pip install 'openboost[distributed]'" - ) - - # Initialize Ray if not already - if not ray.is_initialized(): - ray.init(ignore_reinit_error=True) - - # Determine which GPUs to use - if self.devices is not None: - self.n_gpus = len(self.devices) - else: - n_available = int(ray.available_resources().get('GPU', 0)) - if n_available == 0: - raise RuntimeError( - "No GPUs available. Multi-GPU training requires at least one GPU. " - "Use GradientBoosting without n_gpus for CPU training." - ) - if self.n_gpus is None: - self.n_gpus = n_available - else: - self.n_gpus = min(self.n_gpus, n_available) - self.devices = list(range(self.n_gpus)) - - self.workers = [] - self.shard_sizes = [] - - def setup( - self, - X: NDArray, - y: NDArray, - n_bins: int = 256, - bin_edges: NDArray | None = None, - ): - """Shard data and create GPU workers. - - Args: - X: Training features, shape (n_samples, n_features) - y: Training targets, shape (n_samples,) - n_bins: Number of bins for histogram - bin_edges: Optional pre-computed bin edges for consistent binning - """ - self.n_samples = len(y) - self.n_features = X.shape[1] - - # Compute bin edges globally for consistent binning across shards - if bin_edges is None: - # Use a subset of data to compute bin edges efficiently - sample_size = min(100000, self.n_samples) - if sample_size < self.n_samples: - indices = np.random.choice(self.n_samples, sample_size, replace=False) - X_sample = X[indices] - else: - X_sample = X - - # Compute percentile-based bin edges - self.bin_edges = np.zeros((self.n_features, n_bins + 1), dtype=np.float32) - for f in range(self.n_features): - col = X_sample[:, f] - # Handle NaN values - valid = col[~np.isnan(col)] - if len(valid) > 0: - percentiles = np.linspace(0, 100, n_bins + 1) - self.bin_edges[f] = np.percentile(valid, percentiles) - else: - self.bin_edges[f] = np.linspace(0, 1, n_bins + 1) - else: - self.bin_edges = bin_edges - - # Split data into shards - indices = np.array_split(np.arange(self.n_samples), self.n_gpus) - self.shard_sizes = [len(idx) for idx in indices] - - # Create workers - self.workers = [] - for gpu_id, shard_indices in zip(self.devices, indices, strict=False): - X_shard = X[shard_indices] - y_shard = y[shard_indices] - - worker = GPUWorker.remote( - gpu_id=gpu_id, - X_shard=X_shard, - y_shard=y_shard, - n_bins=n_bins, - bin_edges=self.bin_edges, - ) - self.workers.append(worker) - - def compute_all_gradients( - self, - loss_fn: LossFunction, - ) -> list[tuple[NDArray, NDArray]]: - """Compute gradients on all workers in parallel. - - Args: - loss_fn: Loss function for gradient computation - - Returns: - List of (grad, hess) tuples, one per worker - """ - grad_hess_refs = [ - worker.compute_gradients.remote(loss_fn) - for worker in self.workers - ] - return ray.get(grad_hess_refs) - - def build_all_histograms( - self, - grads_hess: list[tuple[NDArray, NDArray]], - ) -> list[tuple[NDArray, NDArray]]: - """Build local histograms on all workers in parallel. - - Args: - grads_hess: List of (grad, hess) tuples, one per worker - - Returns: - List of (hist_grad, hist_hess) tuples, one per worker - """ - hist_refs = [ - worker.build_histogram.remote(grad, hess) - for worker, (grad, hess) in zip(self.workers, grads_hess, strict=False) - ] - return ray.get(hist_refs) - - def aggregate_histograms( - self, - local_histograms: list[tuple[NDArray, NDArray]], - ) -> tuple[NDArray, NDArray]: - """Sum histograms from all workers (AllReduce). - - Args: - local_histograms: List of (hist_grad, hist_hess) from each worker - - Returns: - Tuple of (global_hist_grad, global_hist_hess) - """ - if not local_histograms: - raise ValueError("No histograms to aggregate") - - # Sum all histograms - global_hist_grad = local_histograms[0][0].copy() - global_hist_hess = local_histograms[0][1].copy() - - for hist_grad, hist_hess in local_histograms[1:]: - global_hist_grad += hist_grad - global_hist_hess += hist_hess - - return global_hist_grad, global_hist_hess - - def update_all_predictions( - self, - tree: TreeStructure, - learning_rate: float, - ): - """Update predictions on all workers with new tree. - - Args: - tree: Fitted tree to add to ensemble - learning_rate: Learning rate for this tree - """ - update_refs = [ - worker.update_predictions.remote(tree, learning_rate) - for worker in self.workers - ] - ray.get(update_refs) # Wait for completion - - def get_all_predictions(self) -> NDArray: - """Collect predictions from all workers and concatenate. - - Returns: - Full prediction array, shape (n_samples,) - """ - pred_refs = [worker.get_predictions.remote() for worker in self.workers] - preds = ray.get(pred_refs) - return np.concatenate(preds) - - def shutdown(self): - """Shutdown workers and cleanup.""" - if self.workers: - for worker in self.workers: - ray.kill(worker) - self.workers = [] - - -# ============================================================================= -# High-level distributed tree fitting -# ============================================================================= - -def fit_tree_multigpu( - ctx: MultiGPUContext, - grads_hess: list[tuple[NDArray, NDArray]], - *, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, - min_gain: float = 0.0, -) -> TreeStructure: - """Fit a single tree using multi-GPU histogram aggregation. - - This is a simplified single-level tree building that: - 1. Builds local histograms on each GPU - 2. Aggregates to global histogram - 3. Uses standard tree building from global histogram - - For full distributed tree building with sample partitioning, - see fit_tree_distributed in _tree.py. - - Args: - ctx: MultiGPUContext with initialized workers - grads_hess: List of (grad, hess) tuples from each worker - max_depth: Maximum tree depth - min_child_weight: Minimum sum of hessian in a leaf - reg_lambda: L2 regularization - reg_alpha: L1 regularization - min_gain: Minimum gain to make a split - - Returns: - Fitted TreeStructure - """ - - # Build local histograms on each GPU - local_histograms = ctx.build_all_histograms(grads_hess) - - # Aggregate histograms - global_hist_grad, global_hist_hess = ctx.aggregate_histograms(local_histograms) - - # For now, we use a simplified approach: - # Build tree using the first worker's data structure but with global histograms - # A more sophisticated approach would do distributed tree building - - # Get gradients and hessians aggregated - total_grad = np.zeros(ctx.n_samples, dtype=np.float32) - total_hess = np.zeros(ctx.n_samples, dtype=np.float32) - - offset = 0 - for (grad, hess), size in zip(grads_hess, ctx.shard_sizes, strict=False): - total_grad[offset:offset + size] = grad - total_hess[offset:offset + size] = hess - offset += size - - # Create a dummy BinnedArray for tree fitting - # In practice, we'd want to do distributed tree building - # For now, collect data to driver and fit there - ray.get([w.get_predictions.remote() for w in ctx.workers]) - - # Use the global histogram for tree building - # This is where we'd integrate with fit_tree_from_histogram - # For now, fall back to standard fit_tree with aggregated data - - # Get binned data from first worker for structure - # NOTE: This is a simplification - full implementation would do - # distributed tree building with sample partitioning - ray.get(ctx.workers[0].get_bin_edges.remote()) - - # Build tree using growth strategy with global histogram - from .._core._growth import GrowthConfig - - config = GrowthConfig( - max_depth=max_depth, - min_child_weight=min_child_weight, - reg_lambda=reg_lambda, - reg_alpha=reg_alpha, - min_gain=min_gain, - ) - - # Create tree structure using histogram-based building - return _build_tree_from_global_histogram( - global_hist_grad, - global_hist_hess, - ctx.n_features, - config, - ) - - -def _build_tree_from_global_histogram( - hist_grad: NDArray, - hist_hess: NDArray, - n_features: int, - config: Any, -) -> TreeStructure: - """Build a tree structure from aggregated global histogram. - - Implements full level-wise tree building up to max_depth using - histogram-based splitting. At each depth, every active node is - evaluated for the best split using its portion of the histogram, - and child histograms are derived via subtraction from the parent. - - Args: - hist_grad: Global gradient histogram, shape (n_features, n_bins) - hist_hess: Global hessian histogram, shape (n_features, n_bins) - n_features: Number of features - config: GrowthConfig with tree building parameters - - Returns: - TreeStructure - """ - from .._core._growth import TreeStructure - from .._core._split import compute_leaf_value, find_best_split - - hist_grad.shape[1] - max_nodes = 2**(config.max_depth + 1) - 1 - - # Initialize tree arrays - features = np.full(max_nodes, -1, dtype=np.int32) - thresholds = np.zeros(max_nodes, dtype=np.int32) - values = np.zeros(max_nodes, dtype=np.float32) - left_children = np.full(max_nodes, -1, dtype=np.int32) - right_children = np.full(max_nodes, -1, dtype=np.int32) - - # Per-node histogram storage: node_id -> (hist_grad, hist_hess, sum_grad, sum_hess) - node_hists: dict[int, tuple[NDArray, NDArray, float, float]] = {} - - # Root histogram comes from the global aggregated histogram - root_sum_grad = float(np.sum(hist_grad)) - root_sum_hess = float(np.sum(hist_hess)) - node_hists[0] = (hist_grad.copy(), hist_hess.copy(), root_sum_grad, root_sum_hess) - - # Check if root itself should just be a leaf - root_split = find_best_split( - hist_grad, hist_hess, - root_sum_grad, root_sum_hess, - reg_lambda=config.reg_lambda, - min_child_weight=config.min_child_weight, - min_gain=config.min_gain, - ) - if not root_split.is_valid: - values[0] = compute_leaf_value(root_sum_grad, root_sum_hess, - config.reg_lambda, config.reg_alpha) - return TreeStructure( - features=features[:1], - thresholds=thresholds[:1], - values=values[:1], - left_children=left_children[:1], - right_children=right_children[:1], - n_nodes=1, - depth=0, - n_features=n_features, - ) - - actual_depth = 0 - - # Level-wise growth loop - for depth in range(config.max_depth): - # Nodes at this depth: indices 2^depth - 1 .. 2^(depth+1) - 2 - level_start = 2**depth - 1 - level_end = 2**(depth + 1) - 1 - - # Collect active nodes at this level (those that have histograms) - active_nodes = [nid for nid in range(level_start, level_end) - if nid in node_hists] - if not active_nodes: - break - - any_split = False - - for node_id in active_nodes: - h_grad, h_hess, s_grad, s_hess = node_hists[node_id] - - split = find_best_split( - h_grad, h_hess, - s_grad, s_hess, - reg_lambda=config.reg_lambda, - min_child_weight=config.min_child_weight, - min_gain=config.min_gain, - ) - - if not split.is_valid: - # This node becomes a leaf - values[node_id] = compute_leaf_value( - s_grad, s_hess, config.reg_lambda, config.reg_alpha) - continue - - any_split = True - actual_depth = max(actual_depth, depth + 1) - - left_child = 2 * node_id + 1 - right_child = 2 * node_id + 2 - features[node_id] = split.feature - thresholds[node_id] = split.threshold - left_children[node_id] = left_child - right_children[node_id] = right_child - - # Derive child histograms from the parent histogram using the - # split point. Left child gets bins [0 .. threshold], right - # child gets bins [threshold+1 .. n_bins-1]. - left_hist_grad = np.zeros_like(h_grad) - left_hist_hess = np.zeros_like(h_hess) - - for _f in range(n_features): - # For the split feature, partition bins at the threshold - # For all other features, we need the full histogram - # conditioned on left/right. With only histogram information - # (no sample-level data on the driver), we can exactly - # partition bins for the split feature but must approximate - # other features. The standard histogram-subtraction trick: - # child_smaller = build from data - # child_larger = parent - child_smaller - # requires per-sample data. Here we use the split feature's - # cumulative sums to derive left/right proportions and scale - # other features proportionally. - pass - - # Exact partition for split feature - sf = split.feature - t = split.threshold - - # Left child: bins <= threshold for the split feature - left_sf_grad = float(np.sum(h_grad[sf, :t + 1])) - left_sf_hess = float(np.sum(h_hess[sf, :t + 1])) - s_grad - left_sf_grad - s_hess - left_sf_hess - - # For each feature, split histogram bins proportionally based on - # the split feature's left/right ratio. - total_weight = s_hess if s_hess > 0 else 1.0 - left_ratio = left_sf_hess / total_weight if total_weight > 0 else 0.5 - - for f in range(n_features): - if f == sf: - # Split feature: exact partitioning - left_hist_grad[f, :t + 1] = h_grad[f, :t + 1] - left_hist_hess[f, :t + 1] = h_hess[f, :t + 1] - else: - # Other features: scale proportionally - left_hist_grad[f] = h_grad[f] * left_ratio - left_hist_hess[f] = h_hess[f] * left_ratio - - # Right child = parent - left child (histogram subtraction) - right_hist_grad = h_grad - left_hist_grad - right_hist_hess = h_hess - left_hist_hess - - left_sum_grad = float(np.sum(left_hist_grad[sf])) - left_sum_hess = float(np.sum(left_hist_hess[sf])) - right_sum_grad = s_grad - left_sum_grad - right_sum_hess = s_hess - left_sum_hess - - # Store child histograms for the next depth level - if left_child < max_nodes: - node_hists[left_child] = (left_hist_grad, left_hist_hess, - left_sum_grad, left_sum_hess) - if right_child < max_nodes: - node_hists[right_child] = (right_hist_grad, right_hist_hess, - right_sum_grad, right_sum_hess) - - if not any_split: - break - - # Remove processed parent histograms to save memory - for node_id in active_nodes: - node_hists.pop(node_id, None) - - # Assign leaf values for any remaining nodes that were never split - for node_id in list(node_hists.keys()): - if features[node_id] == -1 and left_children[node_id] == -1: - _, _, s_grad, s_hess = node_hists[node_id] - values[node_id] = compute_leaf_value( - s_grad, s_hess, config.reg_lambda, config.reg_alpha) - - # Count actual nodes in the tree - n_nodes = 1 - for i in range(max_nodes - 1, 0, -1): - parent = (i - 1) // 2 - if left_children[parent] != -1: - n_nodes = i + 1 - break - - return TreeStructure( - features=features[:n_nodes], - thresholds=thresholds[:n_nodes], - values=values[:n_nodes], - left_children=left_children[:n_nodes], - right_children=right_children[:n_nodes], - n_nodes=n_nodes, - depth=actual_depth, - n_features=n_features, - ) - - -__all__ = [ - "GPUWorkerBase", - "GPUWorker", - "MultiGPUContext", - "fit_tree_multigpu", -] diff --git a/src/openboost/_distributed/_ray.py b/src/openboost/_distributed/_ray.py deleted file mode 100644 index d06473a..0000000 --- a/src/openboost/_distributed/_ray.py +++ /dev/null @@ -1,156 +0,0 @@ -"""Ray-based distributed training backend for OpenBoost. - -Phase 12: Implements distributed training using Ray for multi-GPU/multi-node. -""" - -from typing import Any - -import numpy as np -from numpy.typing import NDArray - -try: - import ray -except ImportError: - ray = None - -import openboost as ob -from openboost._core._primitives import build_node_histograms, partition_samples - - -class RayWorker: - """Worker that holds a data shard and computes local histograms.""" - - def __init__(self, X_shard: NDArray, y_shard: NDArray, n_bins: int, - bin_edges=None): - if bin_edges is not None: - from openboost._array import BinnedArray - n_features = len(bin_edges) - template = BinnedArray( - data=np.empty(0, dtype=np.uint8), - bin_edges=bin_edges, - n_features=n_features, - n_samples=0, - device="cpu", - ) - self.X_binned = template.transform(X_shard) - else: - self.X_binned = ob.array(X_shard, n_bins=n_bins) - self.y = y_shard - self.n_samples = len(y_shard) - # Initialize sample_node_ids locally - self.sample_node_ids = np.zeros(self.n_samples, dtype=np.int32) - # Initialize predictions - self.pred = np.zeros(self.n_samples, dtype=np.float32) - - def compute_histograms(self, grad: NDArray, hess: NDArray, - node_ids: list[int]) -> dict[int, Any]: - """Compute local histograms for this shard.""" - histograms = build_node_histograms( - self.X_binned.data if hasattr(self.X_binned, 'data') else self.X_binned, - grad, hess, self.sample_node_ids, node_ids - ) - return histograms - - def compute_gradients(self, loss_fn: Any) -> tuple[NDArray, NDArray]: - """Compute gradients locally.""" - grad, hess = loss_fn(self.pred, self.y) - return grad.astype(np.float32), hess.astype(np.float32) - - def update_predictions(self, tree: Any, learning_rate: float): - """Update local predictions with new tree.""" - tree_pred = tree(self.X_binned) - self.pred += learning_rate * tree_pred - - def partition_samples(self, splits: dict, node_ids: Any = None): - """Update sample_node_ids based on splits.""" - self.sample_node_ids = partition_samples( - self.X_binned.data if hasattr(self.X_binned, 'data') else self.X_binned, - self.sample_node_ids, - splits - ) - - def get_n_features(self) -> int: - """Get number of features.""" - if hasattr(self.X_binned, 'n_features'): - return self.X_binned.n_features - return self.X_binned.shape[0] - - def get_sample_node_ids(self) -> NDArray: - """Return current sample node IDs.""" - return self.sample_node_ids - - def init_node_ids(self): - """Reset node IDs to 0.""" - self.sample_node_ids[:] = 0 - return self.sample_node_ids - - -# Decorate RayWorker with @ray.remote if ray is available -if ray: - RayWorker = ray.remote(RayWorker) - - -class RayDistributedContext: - """Ray-based distributed context.""" - - def __init__(self, n_workers: int = None): - if not ray: - raise ImportError( - "Distributed training requires Ray. " - "Install with: pip install 'openboost[distributed]'" - ) - - if not ray.is_initialized(): - ray.init(ignore_reinit_error=True) - - self.n_workers = n_workers or int(ray.available_resources().get('GPU', 1)) - if self.n_workers == 0: - self.n_workers = int(ray.available_resources().get('CPU', 1)) - - self.workers = [] - self.rank = 0 - - def setup(self, X: NDArray, y: NDArray, n_bins: int): - """Partition data and create workers.""" - # Compute global bin edges on the driver for consistent binning - global_binned = ob.array(X, n_bins=n_bins) - global_bin_edges = global_binned.bin_edges - - shards = np.array_split(X, self.n_workers) - y_shards = np.array_split(y, self.n_workers) - - self.workers = [ - RayWorker.remote(s, ys, n_bins, bin_edges=global_bin_edges) - for s, ys in zip(shards, y_shards, strict=False) - ] - - def allreduce_histograms(self, local_hists_refs: list[Any]) -> dict[int, Any]: - """Sum histograms from all workers.""" - local_hists = ray.get(local_hists_refs) - - if not local_hists: - return {} - - result = local_hists[0] - - for i in range(1, len(local_hists)): - other = local_hists[i] - for node_id, hist in other.items(): - if node_id not in result: - result[node_id] = hist - else: - # Aggregate - target = result[node_id] - target.hist_grad += hist.hist_grad - target.hist_hess += hist.hist_hess - target.sum_grad += hist.sum_grad - target.sum_hess += hist.sum_hess - target.n_samples += hist.n_samples - - return result - - def broadcast_tree(self, tree: Any) -> Any: - return tree - - def partition_data(self, X: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - raise NotImplementedError("Use setup() for Ray backend") diff --git a/src/openboost/_distributed/_tree.py b/src/openboost/_distributed/_tree.py deleted file mode 100644 index bdc2562..0000000 --- a/src/openboost/_distributed/_tree.py +++ /dev/null @@ -1,157 +0,0 @@ -"""Distributed tree fitting for OpenBoost. - -Phase 12: Implements distributed tree building using histogram aggregation. -""" - -from typing import Any - -import numpy as np - -try: - import ray -except ImportError: - ray = None - -from openboost._core._growth import TreeStructure -from openboost._core._primitives import find_node_splits - - -def fit_tree_distributed( - ctx: Any, # DistributedContext - workers: list[Any], - grad_refs: list[Any], # Ray object refs - hess_refs: list[Any], - *, - max_depth: int = 6, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, - min_gain: float = 0.0, - subsample: float = 1.0, - colsample_bytree: float = 1.0, -) -> TreeStructure: - """Distributed tree fitting (Level-wise).""" - if ray is None: - raise ImportError( - "Distributed training requires Ray. " - "Install with: pip install 'openboost[distributed]'" - ) - - # 1. Initialize - [w.init_node_ids.remote() for w in workers] - - n_features = get_worker_n_features(workers[0]) - - # Initialize tree arrays (similar to LevelWiseGrowth) - max_nodes = 2**(max_depth + 1) - 1 - features = np.full(max_nodes, -1, dtype=np.int32) - thresholds = np.zeros(max_nodes, dtype=np.int32) - values = np.zeros(max_nodes, dtype=np.float32) - left_children = np.full(max_nodes, -1, dtype=np.int32) - right_children = np.full(max_nodes, -1, dtype=np.int32) - - # 2. Grow tree level-wise - active_nodes = [0] - actual_depth = 0 - - for depth in range(max_depth): - if not active_nodes: - break - - actual_depth = depth + 1 - - # 3. Compute local histograms - local_hists_refs = [ - w.compute_histograms.remote(g, h, active_nodes) - for w, g, h in zip(workers, grad_refs, hess_refs, strict=False) - ] - - # 4. Aggregate histograms - global_histograms = ctx.allreduce_histograms(local_hists_refs) - - # 5. Find splits - splits = find_node_splits( - global_histograms, - reg_lambda=reg_lambda, - min_child_weight=min_child_weight, - min_gain=min_gain - ) - - # 6. Apply splits - new_active_nodes = [] - - for node_id, node_split in splits.items(): - features[node_id] = node_split.split.feature - thresholds[node_id] = node_split.split.threshold - left_children[node_id] = node_split.left_child - right_children[node_id] = node_split.right_child - - new_active_nodes.append(node_split.left_child) - new_active_nodes.append(node_split.right_child) - - # 7. Partition samples on workers — must complete before next level - if splits: - partition_refs = [w.partition_samples.remote(splits) for w in workers] - ray.get(partition_refs) # Wait for partitioning to finish - - active_nodes = new_active_nodes - - # 8. Compute leaf values - leaf_nodes = [] - for i in range(max_nodes): - if left_children[i] == -1 and (i == 0 or features[(i-1)//2] >= 0): - leaf_nodes.append(i) - - if leaf_nodes: - local_hists_refs = [ - w.compute_histograms.remote(g, h, leaf_nodes) - for w, g, h in zip(workers, grad_refs, hess_refs, strict=False) - ] - leaf_histograms = ctx.allreduce_histograms(local_hists_refs) - - leaf_vals = compute_leaf_values_from_histograms(leaf_histograms, reg_lambda, reg_alpha) - - for node_id, val in leaf_vals.items(): - values[node_id] = val - - # Trim arrays - n_nodes = count_nodes(left_children) - - return TreeStructure( - features=features[:n_nodes] if n_nodes < max_nodes else features, - thresholds=thresholds[:n_nodes] if n_nodes < max_nodes else thresholds, - values=values[:n_nodes] if n_nodes < max_nodes else values, - left_children=left_children[:n_nodes] if n_nodes < max_nodes else left_children, - right_children=right_children[:n_nodes] if n_nodes < max_nodes else right_children, - n_nodes=n_nodes, - depth=actual_depth, - n_features=n_features, - ) - - -def compute_leaf_values_from_histograms(histograms: dict, reg_lambda: float, reg_alpha: float) -> dict: - from openboost._core._split import compute_leaf_value - result = {} - for node_id, hist in histograms.items(): - result[node_id] = compute_leaf_value(hist.sum_grad, hist.sum_hess, reg_lambda, reg_alpha) - return result - - -def get_worker_n_features(worker): - if ray is None: - raise ImportError( - "Distributed training requires Ray. " - "Install with: pip install 'openboost[distributed]'" - ) - return ray.get(worker.get_n_features.remote()) - - -def count_nodes(left_children): - """Count actual nodes in tree by finding highest valid index.""" - for i in range(len(left_children) - 1, -1, -1): - if i == 0: - return 1 - parent = (i - 1) // 2 - if left_children[parent] != -1: - return i + 1 - return 1 diff --git a/src/openboost/_distributions.py b/src/openboost/_distributions.py deleted file mode 100644 index 7b519f2..0000000 --- a/src/openboost/_distributions.py +++ /dev/null @@ -1,2141 +0,0 @@ -"""Probability distributions for distributional GBDT. - -Phase 15.1: Distribution classes for probabilistic prediction. - -Each distribution defines: -- Parameters (e.g., μ, σ for Normal) -- Link functions (e.g., exp for scale parameters to ensure positivity) -- Negative log-likelihood gradient/hessian per parameter -- Fisher information matrix (for natural gradient / NGBoost) - -Supported distributions: -- Normal (Gaussian): loc, scale -- LogNormal: loc, scale (of underlying normal) -- Gamma: concentration, rate -- Poisson: rate -- NegativeBinomial: mean, dispersion -- StudentT: loc, scale, df -""" - -from __future__ import annotations - -import warnings -from abc import ABC, abstractmethod -from dataclasses import dataclass - -import numpy as np -from numpy.typing import NDArray - -# Type alias for gradient/hessian tuple -GradHess = tuple[NDArray, NDArray] - - -@dataclass -class DistributionOutput: - """Container for distribution parameter predictions. - - Attributes: - params: Dictionary mapping parameter names to predicted values - distribution: The Distribution instance used - """ - params: dict[str, NDArray] - distribution: Distribution - - def mean(self) -> NDArray: - """Expected value E[Y|X].""" - return self.distribution.mean(self.params) - - def variance(self) -> NDArray: - """Variance Var[Y|X].""" - return self.distribution.variance(self.params) - - def std(self) -> NDArray: - """Standard deviation.""" - return np.sqrt(self.variance()) - - def quantile(self, q: float) -> NDArray: - """q-th quantile (0 < q < 1).""" - return self.distribution.quantile(self.params, q) - - def interval(self, alpha: float = 0.1) -> tuple[NDArray, NDArray]: - """(1-alpha) prediction interval. - - Args: - alpha: Significance level (0.1 = 90% interval) - - Returns: - (lower, upper) bounds - """ - lower = self.quantile(alpha / 2) - upper = self.quantile(1 - alpha / 2) - return lower, upper - - def sample(self, n_samples: int = 1, seed: int | None = None) -> NDArray: - """Draw samples from the predicted distribution. - - Args: - n_samples: Number of samples per observation - seed: Random seed for reproducibility - - Returns: - samples: Shape (n_observations, n_samples) - """ - return self.distribution.sample(self.params, n_samples, seed) - - def nll(self, y: NDArray) -> NDArray: - """Negative log-likelihood for observed values. - - Args: - y: Observed values - - Returns: - nll: Per-sample negative log-likelihood - """ - return self.distribution.nll(y, self.params) - - -class Distribution(ABC): - """Base class for probability distributions. - - Subclasses must implement: - - n_params: Number of distributional parameters - - param_names: Names of parameters - - link: Transform raw -> constrained parameter space - - link_inv: Transform constrained -> raw - - nll_gradient: Gradient and hessian of NLL w.r.t. raw parameters - - fisher_information: Fisher information matrix (for NGBoost) - """ - - @property - @abstractmethod - def n_params(self) -> int: - """Number of distributional parameters.""" - pass - - @property - @abstractmethod - def param_names(self) -> list[str]: - """Names of parameters, e.g., ['loc', 'scale'].""" - pass - - @abstractmethod - def link(self, param_name: str, raw: NDArray) -> NDArray: - """Apply link function: raw -> constrained parameter space. - - E.g., for scale: exp(raw) to ensure positivity. - - Args: - param_name: Name of the parameter - raw: Raw (unbounded) values - - Returns: - Constrained parameter values - """ - pass - - @abstractmethod - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - """Inverse link: constrained -> raw (for initialization). - - Args: - param_name: Name of the parameter - param: Constrained parameter values - - Returns: - Raw (unbounded) values - """ - pass - - @abstractmethod - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Compute gradient and hessian of NLL w.r.t. each RAW parameter. - - The gradient is d(NLL)/d(raw), accounting for the link function. - - Args: - y: Observed target values - params: Dictionary of constrained parameter values - - Returns: - Dictionary mapping param_name -> (gradient, hessian) - """ - pass - - @abstractmethod - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information matrix at given parameters. - - Shape: (n_samples, n_params, n_params) - Used for natural gradient computation in NGBoost. - - Args: - params: Dictionary of constrained parameter values - - Returns: - Fisher information matrix - """ - pass - - @property - def exposure_offset(self) -> tuple[str, float] | None: - """Log-exposure offset target: ``(param_name, sign)`` or None. - - When not None, adding ``sign * log(e)`` to the RAW score of - ``param_name`` multiplies the distribution mean by ``e`` (log-link - mean). Used by DistributionalGBDT/NaturalBoost to apply actuarial - exposure offsets. None (the default) means the family has no - log-link mean and exposure is unsupported. - """ - return None - - def _is_diagonal_fisher(self, F: NDArray) -> bool: - """Check if Fisher matrix is diagonal (common for many distributions).""" - n_params = F.shape[1] - if n_params == 1: - return True - # Check multiple samples to avoid false positives from a single sample - check_indices = np.linspace(0, len(F) - 1, min(10, len(F)), dtype=int) - for idx in check_indices: - off_diag_sum = np.sum(np.abs(F[idx])) - np.sum(np.abs(np.diag(F[idx]))) - if off_diag_sum >= 1e-8: - return False - return True - - def natural_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Compute natural gradient: F^{-1} @ ordinary_gradient. - - Natural gradient accounts for the geometry of the parameter space, - leading to faster convergence. This is the key insight of NGBoost. - - Args: - y: Observed target values - params: Dictionary of constrained parameter values - - Returns: - Dictionary mapping param_name -> (natural_gradient, hessian) - """ - # Get ordinary gradients - ord_grads = self.nll_gradient(y, params) - - # Get Fisher matrix - F = self.fisher_information(params) # (n_samples, n_params, n_params) - - # Stack gradients: (n_samples, n_params) - grad_stack = np.stack( - [ord_grads[p][0] for p in self.param_names], - axis=1 - ) - - n_samples = y.shape[0] - n_params = self.n_params - - # Vectorized Fisher inversion based on matrix size - if n_params == 1: - # 1x1: simple reciprocal - natural_grad_stack = grad_stack / np.maximum(F[:, 0, 0:1], 1e-10) - - elif n_params == 2: - # 2x2: analytical inverse (vectorized) - # For [[a,b],[c,d]], inverse is 1/(ad-bc) * [[d,-b],[-c,a]] - a, b = F[:, 0, 0], F[:, 0, 1] - c, d = F[:, 1, 0], F[:, 1, 1] - det = a * d - b * c - det = np.maximum(np.abs(det), 1e-10) * np.sign(det + 1e-20) - - # F_inv @ grad - g0, g1 = grad_stack[:, 0], grad_stack[:, 1] - natural_grad_stack = np.stack([ - (d * g0 - b * g1) / det, - (-c * g0 + a * g1) / det, - ], axis=1) - - elif self._is_diagonal_fisher(F): - # Diagonal Fisher: element-wise division (very common case) - diag = np.diagonal(F, axis1=1, axis2=2) # (n_samples, n_params) - natural_grad_stack = grad_stack / np.maximum(diag, 1e-10) - - else: - # General case: batched solve (still faster than loop) - try: - # np.linalg.solve broadcasts over leading dimensions - natural_grad_stack = np.linalg.solve(F, grad_stack) - except np.linalg.LinAlgError: - # Fallback: regularize and retry - F_reg = F + 1e-6 * np.eye(n_params) - natural_grad_stack = np.linalg.solve(F_reg, grad_stack) - - # Unstack back to dict - # For natural gradient, use identity hessian (standard NGBoost approach) - result = {} - for j, p in enumerate(self.param_names): - result[p] = ( - natural_grad_stack[:, j].astype(np.float32), - np.ones(n_samples, dtype=np.float32), - ) - - return result - - def init_params(self, y: NDArray) -> dict[str, float]: - """Initialize parameters from target values. - - Returns raw (pre-link) initial values for each parameter. - - Args: - y: Target values for initialization - - Returns: - Dictionary mapping param_name -> initial raw value - """ - # Default implementation - subclasses should override - return {p: 0.0 for p in self.param_names} - - @abstractmethod - def mean(self, params: dict[str, NDArray]) -> NDArray: - """Expected value E[Y|params].""" - pass - - @abstractmethod - def variance(self, params: dict[str, NDArray]) -> NDArray: - """Variance Var[Y|params].""" - pass - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - """q-th quantile of the distribution.""" - raise NotImplementedError(f"quantile not implemented for {self.__class__.__name__}") - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - """Sample from the distribution.""" - raise NotImplementedError(f"sample not implemented for {self.__class__.__name__}") - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - """Negative log-likelihood (for evaluation).""" - raise NotImplementedError(f"nll not implemented for {self.__class__.__name__}") - - def validate_target(self, y: NDArray) -> None: # noqa: B027 - """Validate that targets lie in the distribution's support. - - Called at fit-entry (each family's init_params invokes it before - estimating starting values). Raises ValueError on out-of-support - targets instead of silently clipping/rounding them. Internal epsilon - clips remain for numerical safety only. - - Default: no restrictions (real-valued support). - """ - - -# ============================================================================= -# Normal (Gaussian) Distribution -# ============================================================================= - -class Normal(Distribution): - """Normal (Gaussian) distribution. - - Parameters: - loc (μ): Mean, unbounded - scale (σ): Standard deviation, must be positive - - Link functions: - loc: identity (unbounded) - scale: exp (ensures σ > 0) - - PDF: p(y) = (1/√(2πσ²)) exp(-(y-μ)²/(2σ²)) - NLL: 0.5 * log(2πσ²) + (y-μ)²/(2σ²) - """ - - @property - def n_params(self) -> int: - return 2 - - @property - def param_names(self) -> list[str]: - return ['loc', 'scale'] - - def link(self, param_name: str, raw: NDArray) -> NDArray: - if param_name == 'loc': - return raw - elif param_name == 'scale': - # exp with clipping for numerical stability - return np.exp(np.clip(raw, -20, 20)) - raise ValueError(f"Unknown parameter: {param_name}") - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - if param_name == 'loc': - return param - elif param_name == 'scale': - return np.log(np.clip(param, 1e-10, None)) - raise ValueError(f"Unknown parameter: {param_name}") - - def init_params(self, y: NDArray) -> dict[str, float]: - """Initialize with sample mean and std.""" - loc_init = float(np.mean(y)) - scale_init = float(np.std(y)) + 1e-6 - return { - 'loc': loc_init, # Already in raw space (identity link) - 'scale': float(np.log(scale_init)), # Convert to raw (log) space - } - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Compute gradients of NLL w.r.t. raw parameters. - - NLL = 0.5 * log(2πσ²) + (y - μ)² / (2σ²) - - For loc (identity link): - d(NLL)/dμ = -(y - μ) / σ² - d²(NLL)/dμ² = 1 / σ² - - For scale with exp link (σ = exp(s)): - d(NLL)/ds = 1 - (y - μ)² / σ² - d²(NLL)/ds² ≈ 2 (expected hessian at optimum) - """ - μ = params['loc'] - σ = params['scale'] - - residual = y - μ - var = σ ** 2 - - # Location gradients (identity link) - grad_loc = -residual / var - hess_loc = 1.0 / var - - # Scale gradients (exp link: σ = exp(s)) - # Chain rule: d(NLL)/ds = d(NLL)/dσ * dσ/ds = d(NLL)/dσ * σ - # d(NLL)/dσ = 1/σ - (y-μ)²/σ³ - # d(NLL)/ds = σ * (1/σ - (y-μ)²/σ³) = 1 - (y-μ)²/σ² - grad_scale = 1.0 - (residual ** 2) / var - - # Expected hessian (more stable than exact) - # At optimum, (y-μ)² ≈ σ², so d²(NLL)/ds² ≈ 2 - hess_scale = 2.0 * np.ones_like(y) - - return { - 'loc': (grad_loc.astype(np.float32), hess_loc.astype(np.float32)), - 'scale': (grad_scale.astype(np.float32), hess_scale.astype(np.float32)), - } - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information matrix for Normal distribution. - - For Normal with exp link on scale: - F = [[1/σ², 0 ], - [0, 2 ]] - - The off-diagonal is 0 because mean and variance are orthogonal - parameters in the normal family. - """ - n_samples = params['loc'].shape[0] - σ = params['scale'] - - F = np.zeros((n_samples, 2, 2), dtype=np.float32) - F[:, 0, 0] = 1.0 / (σ ** 2) # d²/dμ² - F[:, 1, 1] = 2.0 # d²/ds² (expected, with exp link) - # Off-diagonal is 0 for Normal - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - return params['loc'] - - def variance(self, params: dict[str, NDArray]) -> NDArray: - return params['scale'] ** 2 - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - """Quantile of Normal distribution.""" - from scipy import stats - μ = params['loc'] - σ = params['scale'] - return stats.norm.ppf(q, loc=μ, scale=σ) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - """Sample from Normal distribution.""" - rng = np.random.default_rng(seed) - μ = params['loc'] - σ = params['scale'] - n_obs = μ.shape[0] - return rng.normal(μ[:, None], σ[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - """Negative log-likelihood.""" - μ = params['loc'] - σ = params['scale'] - return 0.5 * np.log(2 * np.pi * σ**2) + (y - μ)**2 / (2 * σ**2) - - -# ============================================================================= -# LogNormal Distribution -# ============================================================================= - -class LogNormal(Distribution): - """Log-Normal distribution for positive continuous data. - - If X ~ LogNormal(μ, σ), then log(X) ~ Normal(μ, σ). - - Parameters: - loc (μ): Mean of underlying normal - scale (σ): Std of underlying normal (must be positive) - - Link functions: - loc: identity - scale: exp - - Mean: exp(μ + σ²/2) - Variance: (exp(σ²) - 1) * exp(2μ + σ²) - """ - - @property - def n_params(self) -> int: - return 2 - - @property - def param_names(self) -> list[str]: - return ['loc', 'scale'] - - def link(self, param_name: str, raw: NDArray) -> NDArray: - if param_name == 'loc': - return raw - elif param_name == 'scale': - return np.exp(np.clip(raw, -20, 20)) - raise ValueError(f"Unknown parameter: {param_name}") - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - if param_name == 'loc': - return param - elif param_name == 'scale': - return np.log(np.clip(param, 1e-10, None)) - raise ValueError(f"Unknown parameter: {param_name}") - - def validate_target(self, y: NDArray) -> None: - y = np.asarray(y) - if np.any(y <= 0): - raise ValueError( - "LogNormal distribution requires strictly positive targets; " - f"got min(y)={float(np.min(y))}. Remove or shift non-positive values." - ) - - def init_params(self, y: NDArray) -> dict[str, float]: - """Initialize from positive target values.""" - self.validate_target(y) - log_y = np.log(np.clip(y, 1e-10, None)) - loc_init = float(np.mean(log_y)) - scale_init = float(np.std(log_y)) + 1e-6 - return { - 'loc': loc_init, - 'scale': float(np.log(scale_init)), - } - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Gradients for LogNormal. - - NLL = log(y) + 0.5*log(2πσ²) + (log(y) - μ)²/(2σ²) - - Same gradients as Normal but with log(y) as target. - """ - μ = params['loc'] - σ = params['scale'] - - log_y = np.log(np.clip(y, 1e-10, None)) - residual = log_y - μ - var = σ ** 2 - - grad_loc = -residual / var - hess_loc = 1.0 / var - - grad_scale = 1.0 - (residual ** 2) / var - hess_scale = 2.0 * np.ones_like(y) - - return { - 'loc': (grad_loc.astype(np.float32), hess_loc.astype(np.float32)), - 'scale': (grad_scale.astype(np.float32), hess_scale.astype(np.float32)), - } - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Same as Normal (parameters are for underlying normal).""" - n_samples = params['loc'].shape[0] - σ = params['scale'] - - F = np.zeros((n_samples, 2, 2), dtype=np.float32) - F[:, 0, 0] = 1.0 / (σ ** 2) - F[:, 1, 1] = 2.0 - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - μ = params['loc'] - σ = params['scale'] - return np.exp(μ + σ**2 / 2) - - def variance(self, params: dict[str, NDArray]) -> NDArray: - μ = params['loc'] - σ = params['scale'] - return (np.exp(σ**2) - 1) * np.exp(2*μ + σ**2) - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - from scipy import stats - μ = params['loc'] - σ = params['scale'] - return stats.lognorm.ppf(q, s=σ, scale=np.exp(μ)) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - rng = np.random.default_rng(seed) - μ = params['loc'] - σ = params['scale'] - n_obs = μ.shape[0] - return rng.lognormal(μ[:, None], σ[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - μ = params['loc'] - σ = params['scale'] - log_y = np.log(np.clip(y, 1e-10, None)) - return log_y + 0.5 * np.log(2 * np.pi * σ**2) + (log_y - μ)**2 / (2 * σ**2) - - -# ============================================================================= -# Gamma Distribution -# ============================================================================= - -class Gamma(Distribution): - """Gamma distribution for positive continuous data. - - Parameterization: shape (α) and rate (β) - - Mean = α/β - - Variance = α/β² - - Parameters: - concentration (α): Shape parameter, must be positive - rate (β): Rate parameter, must be positive - - Link functions: exp for both (ensure positivity) - - Alternative: Can also be parameterized by mean and dispersion. - """ - - @property - def n_params(self) -> int: - return 2 - - @property - def param_names(self) -> list[str]: - return ['concentration', 'rate'] - - @property - def exposure_offset(self) -> tuple[str, float] | None: - # mean = concentration / rate: subtracting log(e) from the raw rate - # scales the mean by e while keeping the shape (concentration) fixed, - # i.e. Y -> e*Y with constant coefficient of variation — the standard - # Gamma GLM exposure treatment. - return ('rate', -1.0) - - def link(self, param_name: str, raw: NDArray) -> NDArray: - # Both parameters must be positive - return np.exp(np.clip(raw, -20, 20)) - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - return np.log(np.clip(param, 1e-10, None)) - - def validate_target(self, y: NDArray) -> None: - y = np.asarray(y) - if np.any(y <= 0): - raise ValueError( - "Gamma distribution requires strictly positive targets; " - f"got min(y)={float(np.min(y))}. Remove or shift non-positive values." - ) - - def init_params(self, y: NDArray) -> dict[str, float]: - """Initialize using method of moments. - - mean = α/β, var = α/β² - => β = mean/var, α = mean * β = mean²/var - """ - self.validate_target(y) - y_clip = np.clip(y, 1e-10, None) - mean_y = float(np.mean(y_clip)) - var_y = float(np.var(y_clip)) + 1e-6 - - rate = mean_y / var_y - concentration = mean_y * rate - - return { - 'concentration': float(np.log(max(concentration, 1e-6))), - 'rate': float(np.log(max(rate, 1e-6))), - } - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Gradients for Gamma distribution. - - NLL = -α*log(β) + log(Γ(α)) - (α-1)*log(y) + β*y - - d(NLL)/dα = -log(β) + ψ(α) - log(y) - d(NLL)/dβ = -α/β + y - - With exp links (α = exp(a), β = exp(b)): - d(NLL)/da = α * (-log(β) + ψ(α) - log(y)) - d(NLL)/db = β * (-α/β + y) = -α + β*y - """ - from scipy.special import digamma, polygamma - - α = params['concentration'] - β = params['rate'] - - log_y = np.log(np.clip(y, 1e-10, None)) - log_β = np.log(β) - - # Gradient w.r.t. raw concentration (with exp link) - grad_conc_raw = α * (-log_β + digamma(α) - log_y) - # Expected hessian approximation - hess_conc = α * polygamma(1, α) * α # α² * ψ'(α) - hess_conc = np.clip(hess_conc, 0.1, 100) # Stability - - # Gradient w.r.t. raw rate (with exp link) - grad_rate_raw = -α + β * y - hess_rate = α # Expected hessian: α - - return { - 'concentration': (grad_conc_raw.astype(np.float32), hess_conc.astype(np.float32)), - 'rate': (grad_rate_raw.astype(np.float32), hess_rate.astype(np.float32)), - } - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information for Gamma (with exp links).""" - from scipy.special import polygamma - - n_samples = params['concentration'].shape[0] - α = params['concentration'] - - F = np.zeros((n_samples, 2, 2), dtype=np.float32) - # F[α,α] = α² * ψ'(α) (trigamma) - F[:, 0, 0] = α**2 * polygamma(1, α) - # F[β,β] = α (with exp link) - F[:, 1, 1] = α - # F[α,β] = -α (but small, often ignored) - F[:, 0, 1] = -α - F[:, 1, 0] = -α - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - α = params['concentration'] - β = params['rate'] - return α / β - - def variance(self, params: dict[str, NDArray]) -> NDArray: - α = params['concentration'] - β = params['rate'] - return α / (β ** 2) - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - from scipy import stats - α = params['concentration'] - β = params['rate'] - return stats.gamma.ppf(q, a=α, scale=1/β) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - rng = np.random.default_rng(seed) - α = params['concentration'] - β = params['rate'] - n_obs = α.shape[0] - return rng.gamma(α[:, None], 1/β[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - from scipy.special import gammaln - α = params['concentration'] - β = params['rate'] - y_clip = np.clip(y, 1e-10, None) - return -α * np.log(β) + gammaln(α) - (α - 1) * np.log(y_clip) + β * y_clip - - -# ============================================================================= -# Poisson Distribution -# ============================================================================= - -def _validate_count_target(y: NDArray, dist_name: str) -> None: - """Shared support check for count distributions (Poisson, NegativeBinomial).""" - y = np.asarray(y) - if np.any(y < 0): - raise ValueError( - f"{dist_name} distribution requires non-negative count targets; " - f"got min(y)={float(np.min(y))}." - ) - # Allow tiny float noise (e.g. from float32 casts), reject genuine non-integers - tol = 1e-6 * np.maximum(1.0, np.abs(y)) - if np.any(np.abs(y - np.round(y)) > tol): - bad = float(y[np.abs(y - np.round(y)) > tol].flat[0]) - raise ValueError( - f"{dist_name} distribution requires integer count targets; " - f"got non-integer value {bad}." - ) - - -class Poisson(Distribution): - """Poisson distribution for count data. - - Single parameter: rate (λ) - - Mean = λ - - Variance = λ - - Link function: exp (ensures λ > 0) - """ - - @property - def n_params(self) -> int: - return 1 - - @property - def param_names(self) -> list[str]: - return ['rate'] - - @property - def exposure_offset(self) -> tuple[str, float] | None: - # mean = rate (log link): adding log(e) to the raw rate scales it by e - return ('rate', 1.0) - - def link(self, param_name: str, raw: NDArray) -> NDArray: - return np.exp(np.clip(raw, -20, 20)) - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - return np.log(np.clip(param, 1e-10, None)) - - def validate_target(self, y: NDArray) -> None: - _validate_count_target(y, "Poisson") - - def init_params(self, y: NDArray) -> dict[str, float]: - self.validate_target(y) - mean_y = float(np.mean(np.clip(y, 0, None))) + 1e-6 - return {'rate': float(np.log(mean_y))} - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Gradients for Poisson. - - NLL = λ - y*log(λ) + log(y!) - d(NLL)/dλ = 1 - y/λ - - With exp link (λ = exp(l)): - d(NLL)/dl = λ - y - d²(NLL)/dl² = λ - """ - λ = params['rate'] - - grad = λ - y - hess = np.maximum(λ, 1e-6) # Hessian = λ - - return { - 'rate': (grad.astype(np.float32), hess.astype(np.float32)), - } - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information for Poisson: F = λ.""" - n_samples = params['rate'].shape[0] - λ = params['rate'] - - F = np.zeros((n_samples, 1, 1), dtype=np.float32) - F[:, 0, 0] = λ - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - return params['rate'] - - def variance(self, params: dict[str, NDArray]) -> NDArray: - return params['rate'] - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - from scipy import stats - λ = params['rate'] - return stats.poisson.ppf(q, mu=λ) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - rng = np.random.default_rng(seed) - λ = params['rate'] - n_obs = λ.shape[0] - return rng.poisson(λ[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - from scipy.special import gammaln - λ = params['rate'] - y_int = np.round(np.clip(y, 0, None)) - return λ - y_int * np.log(np.clip(λ, 1e-10, None)) + gammaln(y_int + 1) - - -# ============================================================================= -# Student-t Distribution -# ============================================================================= - -class StudentT(Distribution): - """Student-t distribution for heavy-tailed data. - - Parameters: - loc (μ): Location parameter - scale (σ): Scale parameter (positive) - df (ν): Degrees of freedom (positive, typically > 2) - - For ν → ∞, approaches Normal distribution. - Lower ν = heavier tails. - - Link functions: - loc: identity - scale: exp - df: softplus (ensures > 0, typically > 2) - """ - - @property - def n_params(self) -> int: - return 3 - - @property - def param_names(self) -> list[str]: - return ['loc', 'scale', 'df'] - - def link(self, param_name: str, raw: NDArray) -> NDArray: - if param_name == 'loc': - return raw - elif param_name == 'scale': - return np.exp(np.clip(raw, -20, 20)) - elif param_name == 'df': - # Softplus + offset to keep df > 2 (ensures finite variance) - return 2.0 + np.log1p(np.exp(np.clip(raw, -20, 20))) - raise ValueError(f"Unknown parameter: {param_name}") - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - if param_name == 'loc': - return param - elif param_name == 'scale': - return np.log(np.clip(param, 1e-10, None)) - elif param_name == 'df': - # Inverse of softplus + offset - return np.log(np.exp(np.clip(param - 2.0, 1e-10, None)) - 1) - raise ValueError(f"Unknown parameter: {param_name}") - - def init_params(self, y: NDArray) -> dict[str, float]: - loc_init = float(np.median(y)) # Median is more robust - scale_init = float(np.std(y)) + 1e-6 - df_init = 10.0 # Start with moderate tails - return { - 'loc': loc_init, - 'scale': float(np.log(scale_init)), - 'df': float(np.log(np.exp(df_init - 2.0) - 1)), - } - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Gradients for Student-t (simplified, using expected hessians).""" - from scipy.special import digamma - - μ = params['loc'] - σ = params['scale'] - ν = params['df'] - - z = (y - μ) / σ - z2 = z ** 2 - - # Weight for each observation - w = (ν + 1) / (ν + z2) - - # Location gradient - grad_loc = -w * z / σ - hess_loc = (ν + 1) / ((ν + 3) * σ**2) # Expected hessian - - # Scale gradient (with exp link) - grad_scale = 1 - w * z2 - hess_scale = 2.0 * np.ones_like(y) # Approximation - - # DF gradient: exact chain rule through the softplus+2 link - # ν = 2 + softplus(raw) => dν/d(raw) = sigmoid(raw) = 1 - exp(-(ν - 2)) - # d(NLL)/dν = 0.5 * [ψ(ν/2) - ψ((ν+1)/2) + 1/ν + log(1 + z²/ν) - # - (ν+1)z² / (ν(ν+z²))] - df_safe = np.maximum(ν, 2.0 + 1e-6) - dnll_dnu = 0.5 * ( - digamma(df_safe / 2) - digamma((df_safe + 1) / 2) - + 1.0 / df_safe - + np.log1p(z2 / df_safe) - - (df_safe + 1) * z2 / (df_safe * (df_safe + z2)) - ) - grad_df = dnll_dnu * (1.0 - np.exp(-(df_safe - 2.0))) - hess_df = np.maximum(np.abs(grad_df), 0.1) * np.ones_like(y) - - return { - 'loc': (grad_loc.astype(np.float32), hess_loc.astype(np.float32)), - 'scale': (grad_scale.astype(np.float32), hess_scale.astype(np.float32)), - 'df': (grad_df.astype(np.float32), hess_df.astype(np.float32)), - } - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information for Student-t (diagonal approximation).""" - n_samples = params['loc'].shape[0] - σ = params['scale'] - ν = params['df'] - - F = np.zeros((n_samples, 3, 3), dtype=np.float32) - F[:, 0, 0] = (ν + 1) / ((ν + 3) * σ**2) - F[:, 1, 1] = 2.0 - F[:, 2, 2] = 0.1 # Small, df is hard to estimate - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - return params['loc'] # For ν > 1 - - def variance(self, params: dict[str, NDArray]) -> NDArray: - σ = params['scale'] - ν = params['df'] - # Variance = σ² * ν / (ν - 2) for ν > 2 - return σ**2 * ν / np.maximum(ν - 2, 1e-6) - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - from scipy import stats - μ = params['loc'] - σ = params['scale'] - ν = params['df'] - return stats.t.ppf(q, df=ν, loc=μ, scale=σ) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - rng = np.random.default_rng(seed) - μ = params['loc'] - σ = params['scale'] - ν = params['df'] - n_obs = μ.shape[0] - return μ[:, None] + σ[:, None] * rng.standard_t(ν[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - from scipy.special import gammaln - μ = params['loc'] - σ = params['scale'] - ν = params['df'] - z = (y - μ) / σ - return ( - gammaln((ν + 1) / 2) - gammaln(ν / 2) - - 0.5 * np.log(ν * np.pi) - np.log(σ) - - (ν + 1) / 2 * np.log(1 + z**2 / ν) - ) * -1 # Negative because we computed log-likelihood - - -# ============================================================================= -# Tweedie Distribution (Kaggle Insurance Competitions!) -# ============================================================================= - -class Tweedie(Distribution): - """Tweedie distribution for zero-inflated positive continuous data. - - **Key use case**: Insurance claims, revenue forecasting with zeros. - - Popular in Kaggle competitions: - - Porto Seguro Safe Driver Prediction - - Allstate Claims Severity - - Any competition with zero-inflated positive targets - - The Tweedie distribution is a compound Poisson-Gamma: - - ρ = 1: Poisson (count data) - - 1 < ρ < 2: Compound Poisson-Gamma (zeros + positive continuous) - - ρ = 2: Gamma (positive continuous) - - Parameters: - mu (μ): Mean parameter (positive) - phi (φ): Dispersion parameter (positive) - - Why better than XGBoost? - - XGBoost Tweedie only outputs point estimates - - NGBoost Tweedie outputs full distribution → prediction intervals, - uncertainty quantification, probabilistic forecasts - - Link functions: - mu: log (ensures μ > 0) - phi: log (ensures φ > 0) - """ - - def __init__(self, power: float = 1.5): - """Initialize Tweedie with power parameter. - - Args: - power: Variance power (1 < power < 2 for compound Poisson-Gamma) - 1.5 is the default used in most Kaggle competitions. - """ - self.power = power - - @property - def n_params(self) -> int: - return 2 - - @property - def param_names(self) -> list[str]: - return ['mu', 'phi'] - - @property - def exposure_offset(self) -> tuple[str, float] | None: - # mean = mu (log link): adding log(e) to the raw mu scales it by e - return ('mu', 1.0) - - def link(self, param_name: str, raw: NDArray) -> NDArray: - # Both parameters must be positive - return np.exp(np.clip(raw, -20, 20)) - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - return np.log(np.clip(param, 1e-10, None)) - - def validate_target(self, y: NDArray) -> None: - y = np.asarray(y) - if np.any(y < 0): - raise ValueError( - "Tweedie distribution requires non-negative targets; " - f"got min(y)={float(np.min(y))}." - ) - - def init_params(self, y: NDArray) -> dict[str, float]: - """Initialize from target values. - - For Tweedie, μ = E[Y], and φ is estimated from variance. - """ - self.validate_target(y) - y_clip = np.clip(y, 1e-10, None) - mu_init = float(np.mean(y_clip)) + 1e-6 - - # Estimate dispersion: Var(Y) = φ * μ^ρ - var_y = float(np.var(y_clip)) + 1e-6 - phi_init = var_y / (mu_init ** self.power) + 1e-6 - - return { - 'mu': float(np.log(mu_init)), - 'phi': float(np.log(phi_init)), - } - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Gradients for Tweedie distribution. - - Using the deviance formulation (standard in GLMs). - - For Tweedie with power ρ: - d(NLL)/dμ = (μ^(1-ρ) - y*μ^(-ρ)) / φ - """ - μ = params['mu'] - φ = params['phi'] - ρ = self.power - - # Ensure numerical stability - μ_safe = np.clip(μ, 1e-10, 1e10) - y_safe = np.clip(y, 0, None) - - # Gradient w.r.t. log(μ) (with log link) - # d(NLL)/d(log μ) = μ * d(NLL)/dμ - mu_pow_1_rho = np.power(μ_safe, 1 - ρ) - mu_pow_neg_rho = np.power(μ_safe, -ρ) - - grad_mu_raw = μ_safe * (mu_pow_1_rho - y_safe * mu_pow_neg_rho) / φ - - # Expected hessian approximation - hess_mu = μ_safe ** (2 - ρ) / φ - hess_mu = np.clip(hess_mu, 1e-6, 1e6) - - # Gradient w.r.t. log(φ) - # Dispersion affects the scale but is harder to estimate - # Use simple gradient: d(NLL)/d(log φ) ≈ 1 - deviance/φ - deviance = self._compute_deviance(y_safe, μ_safe) - grad_phi_raw = 1.0 - deviance / (2 * φ) - hess_phi = 0.5 * np.ones_like(y) # Conservative hessian - - return { - 'mu': (grad_mu_raw.astype(np.float32), hess_mu.astype(np.float32)), - 'phi': (grad_phi_raw.astype(np.float32), hess_phi.astype(np.float32)), - } - - def _compute_deviance(self, y: NDArray, mu: NDArray) -> NDArray: - """Compute Tweedie deviance.""" - ρ = self.power - mu_safe = np.clip(mu, 1e-10, None) - - if ρ == 1: # Poisson - y_safe = np.clip(y, 1e-10, None) - return 2 * (y_safe * np.log(y_safe / mu_safe) - (y_safe - mu_safe)) - elif ρ == 2: # Gamma - y_safe = np.clip(y, 1e-10, None) - return 2 * (np.log(mu_safe / y_safe) + (y_safe - mu_safe) / mu_safe) - else: - # General Tweedie (1 < ρ < 2) - # Handle y=0 as a special case: when y=0, the y-dependent terms vanish - y_pos = np.clip(y, 1e-10, None) - term1 = np.power(y_pos, 2 - ρ) / ((1 - ρ) * (2 - ρ)) - term2 = y_pos * np.power(mu_safe, 1 - ρ) / (1 - ρ) - term3 = np.power(mu_safe, 2 - ρ) / (2 - ρ) - deviance_ypos = 2 * (term1 - term2 + term3) - # When y=0, deviance = 2 * mu^(2-ρ) / (2-ρ) - deviance_yzero = 2 * np.power(mu_safe, 2 - ρ) / (2 - ρ) - return np.where(y == 0, deviance_yzero, deviance_ypos) - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information for Tweedie.""" - n_samples = params['mu'].shape[0] - μ = params['mu'] - φ = params['phi'] - ρ = self.power - - F = np.zeros((n_samples, 2, 2), dtype=np.float32) - F[:, 0, 0] = μ ** (2 - ρ) / φ - F[:, 1, 1] = 0.5 - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - return params['mu'] - - def variance(self, params: dict[str, NDArray]) -> NDArray: - μ = params['mu'] - φ = params['phi'] - return φ * np.power(μ, self.power) - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - """Quantile respecting the compound Poisson-Gamma support. - - For 1 < power < 2 the distribution has a point mass - P(Y=0) = exp(-λ) with λ = μ^(2-ρ)/(φ(2-ρ)): - - q <= P(Y=0): the quantile is exactly 0 - - q > P(Y=0): the conditional positive part Y | Y > 0 is - approximated by a Gamma matched to its conditional mean/variance - (derived from the unconditional mean μ, variance φμ^ρ, and the - zero mass). - """ - from scipy import stats - μ = params['mu'] - φ = params['phi'] - ρ = self.power - - if not (1 < ρ < 2): - # Outside the compound Poisson-Gamma range: Normal approximation - # clipped to the non-negative support - σ = np.sqrt(self.variance(params)) - return np.maximum(stats.norm.ppf(q, loc=μ, scale=σ), 0.0) - - λ = np.power(μ, 2 - ρ) / (φ * (2 - ρ)) - p0 = np.exp(-λ) - - # Conditional moments of Y | Y > 0: - # E[Y] = (1-p0) * m₊ and E[Y²] = (1-p0) * E[Y²|Y>0] - var = φ * np.power(μ, ρ) - one_minus_p0 = np.maximum(1.0 - p0, 1e-12) - mean_pos = μ / one_minus_p0 - var_pos = (var + μ ** 2) / one_minus_p0 - mean_pos ** 2 - var_pos = np.maximum(var_pos, 1e-12) - - shape = mean_pos ** 2 / var_pos - scale = var_pos / mean_pos - q_adj = np.clip((q - p0) / one_minus_p0, 0.0, 1.0) - positive_part = stats.gamma.ppf(q_adj, a=shape, scale=scale) - - return np.where(q <= p0, 0.0, positive_part) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - """Sample from Tweedie using compound Poisson-Gamma (vectorized). - - Draws N ~ Poisson(λ) for every (observation, sample) cell at once, - then uses the fact that a sum of N iid Gamma(α, scale) variables is - Gamma(N·α, scale) — no per-cell Python loops. - """ - rng = np.random.default_rng(seed) - μ = params['mu'] - φ = params['phi'] - ρ = self.power - n_obs = μ.shape[0] - - λ = np.power(μ, 2 - ρ) / (φ * (2 - ρ)) # Poisson rate - α = (2 - ρ) / (ρ - 1) # Gamma shape per claim - scale = φ * (ρ - 1) * np.power(μ, ρ - 1) # Gamma scale - - counts = rng.poisson(λ[:, None], size=(n_obs, n_samples)) - # shape=0 is invalid for rng.gamma; use a tiny floor, then zero out N=0 cells - shapes = np.maximum(counts * α, 1e-12) - samples = rng.gamma(shapes, np.broadcast_to(scale[:, None], (n_obs, n_samples))) - samples[counts == 0] = 0.0 - - return samples.astype(np.float32) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - """Proper negative log-likelihood (predict-time scoring). - - For 1 < power < 2, evaluates the exact compound Poisson-Gamma - density: -log P(Y=0) = λ = μ^(2-ρ)/(φ(2-ρ)) at y == 0, and the - Dunn & Smyth (2005) series expansion of the density for y > 0 - (logsumexp over the W_j terms, with the summation range sized from - the dominant index j_max ≈ y^(2-ρ)/(φ(2-ρ))). - - Note: the TRAINING objective (nll_gradient) intentionally remains - deviance-based — changing it would change model fits. This method is - only used for evaluation/scoring. - """ - from scipy.special import gammaln, logsumexp - - μ = np.clip(np.asarray(params['mu'], dtype=np.float64), 1e-10, 1e10) - φ = np.clip(np.asarray(params['phi'], dtype=np.float64), 1e-10, 1e10) - ρ = self.power - y = np.asarray(y, dtype=np.float64) - - if not (1 < ρ < 2): - # ρ=1 (Poisson lattice) and ρ=2 (Gamma) boundaries keep the - # historical deviance-based quantity - deviance = self._compute_deviance(np.clip(y, 0, None), μ) - return deviance / (2 * φ) - - λ = np.power(μ, 2 - ρ) / (φ * (2 - ρ)) - out = λ.copy() # y == 0 case: NLL = -log P(Y=0) = λ - - pos = y > 0 - if np.any(pos): - yp = y[pos] - mp = μ[pos] - php = φ[pos] - - α = (2 - ρ) / (ρ - 1) - θ = np.power(mp, 1 - ρ) / (1 - ρ) # canonical parameter - κ = np.power(mp, 2 - ρ) / (2 - ρ) # cumulant function - - # log W_j = j*A - lgamma(j+1) - lgamma(j*α), per-sample A - A = ( - α * np.log(yp) - - α * np.log(ρ - 1) - - (1 + α) * np.log(php) - - np.log(2 - ρ) - ) - # Terms peak near j_max; sum well past the largest peak in the batch - j_max = np.maximum(np.power(yp, 2 - ρ) / (php * (2 - ρ)), 1.0) - n_terms = int(min(max(np.ceil(3 * j_max.max()) + 40, 60), 20000)) - j = np.arange(1, n_terms + 1, dtype=np.float64)[:, None] - - log_w = j * A[None, :] - gammaln(j + 1) - gammaln(j * α) - log_series = logsumexp(log_w, axis=0) - - # log f(y) = -log(y) + log Σ_j W_j + (yθ - κ(θ))/φ - log_pdf = -np.log(yp) + log_series + (yp * θ - κ) / php - out[pos] = -log_pdf - - return out - - -# ============================================================================= -# Negative Binomial Distribution (Kaggle Count Data Competitions!) -# ============================================================================= - -class NegativeBinomial(Distribution): - """Negative Binomial distribution for overdispersed count data. - - **Key use case**: Sales forecasting, demand prediction, click counts. - - Popular in Kaggle competitions: - - Rossmann Store Sales - - Bike Sharing Demand - - Grupo Bimbo Inventory Demand - - Any competition with count data where variance > mean - - Compared to Poisson: - - Poisson: Var(Y) = Mean(Y) - - NegBin: Var(Y) = Mean(Y) + Mean(Y)²/r (overdispersion) - - Parameters: - mu (μ): Mean parameter (positive) - r: Dispersion parameter (positive, smaller = more overdispersion) - - Why better than XGBoost? - - XGBoost can't output count distributions at all - - NGBoost NegBin outputs full distribution → prediction intervals, - probability of exceeding thresholds, demand planning - - Link functions: - mu: log (ensures μ > 0) - r: log (ensures r > 0) - """ - - @property - def n_params(self) -> int: - return 2 - - @property - def param_names(self) -> list[str]: - return ['mu', 'r'] - - @property - def exposure_offset(self) -> tuple[str, float] | None: - # mean = mu (log link): adding log(e) to the raw mu scales it by e - return ('mu', 1.0) - - def link(self, param_name: str, raw: NDArray) -> NDArray: - return np.exp(np.clip(raw, -20, 20)) - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - return np.log(np.clip(param, 1e-10, None)) - - def validate_target(self, y: NDArray) -> None: - _validate_count_target(y, "NegativeBinomial") - - def init_params(self, y: NDArray) -> dict[str, float]: - """Initialize using method of moments. - - Mean = μ - Var = μ + μ²/r - => r = μ² / (Var - μ) - """ - self.validate_target(y) - y_clip = np.clip(y, 0, None) - mu_init = float(np.mean(y_clip)) + 1e-6 - var_y = float(np.var(y_clip)) + 1e-6 - - # Estimate r from method of moments - if var_y > mu_init: # noqa: SIM108 - r_init = mu_init ** 2 / (var_y - mu_init) - else: - r_init = 10.0 # Default if not overdispersed - - r_init = np.clip(r_init, 0.1, 1000) - - return { - 'mu': float(np.log(mu_init)), - 'r': float(np.log(r_init)), - } - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Gradients for Negative Binomial. - - NLL = -log Γ(y+r) + log Γ(r) + log Γ(y+1) - - r*log(r/(r+μ)) - y*log(μ/(r+μ)) - """ - from scipy.special import digamma, polygamma - - μ = params['mu'] - r = params['r'] - - # Ensure numerical stability - μ_safe = np.clip(μ, 1e-10, 1e10) - r_safe = np.clip(r, 1e-10, 1e10) - y_safe = np.clip(y, 0, None) - - # Common terms - p = r_safe / (r_safe + μ_safe) # Success probability - - # Gradient w.r.t. log(μ) - # d(loglik)/dμ = p*(y - μ)/μ => d(NLL)/dμ = p*(μ - y)/μ - # d(NLL)/d(log μ) = μ * d(NLL)/dμ = (μ - y) * p - grad_mu_raw = (μ_safe - y_safe) * p - - # Expected hessian - hess_mu = μ_safe * (1 - p) - hess_mu = np.clip(hess_mu, 1e-6, 1e6) - - # Gradient w.r.t. log(r) (dispersion) - # loglik = lgamma(y+r) - lgamma(r) + r*log(p) + y*log(1-p) + const - # d(loglik)/dr = digamma(y+r) - digamma(r) + log(p) + (1-p) - y/(r+μ) - # d(NLL)/d(log r) = -r * d(loglik)/dr - grad_r_raw = -r_safe * ( - digamma(y_safe + r_safe) - digamma(r_safe) - + np.log(p) + (1 - p) - y_safe / (r_safe + μ_safe) - ) - hess_r = r_safe * polygamma(1, r_safe) # Expected hessian - hess_r = np.clip(hess_r, 0.1, 10) - - return { - 'mu': (grad_mu_raw.astype(np.float32), hess_mu.astype(np.float32)), - 'r': (grad_r_raw.astype(np.float32), hess_r.astype(np.float32)), - } - - def fisher_information( - self, - params: dict[str, NDArray], - ) -> NDArray: - """Fisher information for Negative Binomial.""" - from scipy.special import polygamma - - n_samples = params['mu'].shape[0] - μ = params['mu'] - r = params['r'] - - p = r / (r + μ) - - F = np.zeros((n_samples, 2, 2), dtype=np.float32) - F[:, 0, 0] = μ * (1 - p) - F[:, 1, 1] = np.clip(r * polygamma(1, r), 0.1, 10) - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - return params['mu'] - - def variance(self, params: dict[str, NDArray]) -> NDArray: - μ = params['mu'] - r = params['r'] - return μ + μ ** 2 / r - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - from scipy import stats - μ = params['mu'] - r = params['r'] - p = r / (r + μ) - return stats.nbinom.ppf(q, n=r, p=p) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - rng = np.random.default_rng(seed) - μ = params['mu'] - r = params['r'] - n_obs = μ.shape[0] - - p = r / (r + μ) - return rng.negative_binomial(r[:, None], p[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - from scipy.special import gammaln - μ = params['mu'] - r = params['r'] - y_int = np.round(np.clip(y, 0, None)) - - # NLL = -(gammaln(y+r) - gammaln(r) - gammaln(y+1) - # + r*log(r/(r+μ)) + y*log(μ/(r+μ))) - return -( - gammaln(y_int + r) - gammaln(r) - gammaln(y_int + 1) - + r * np.log(r / (r + μ)) - + y_int * np.log(μ / (r + μ)) - ) - - def prob_exceed(self, params: dict[str, NDArray], threshold: float) -> NDArray: - """Probability that Y > threshold. - - Very useful for demand planning: "What's the probability we need - more than 100 units?" - """ - from scipy import stats - μ = params['mu'] - r = params['r'] - p = r / (r + μ) - return 1 - stats.nbinom.cdf(threshold, n=r, p=p) - - -# ============================================================================= -# Custom Distribution with Autodiff (Define Your Own!) -# ============================================================================= - -class CustomDistribution(Distribution): - """User-defined distribution with automatic gradient computation. - - Define any parametric distribution by specifying: - 1. Parameter names and link functions - 2. Negative log-likelihood function - - Gradients are computed automatically via: - - JAX (if available) - fastest - - Numerical differentiation (fallback) - - Example: Custom "ratio" distribution y ~ Normal(A*(1-B)/C, σ) - - >>> def my_nll(y, params): - ... A, B, C, sigma = params['A'], params['B'], params['C'], params['sigma'] - ... mu = A * (1 - B) / C - ... return 0.5 * np.log(2 * np.pi * sigma**2) + (y - mu)**2 / (2 * sigma**2) - >>> - >>> dist = CustomDistribution( - ... param_names=['A', 'B', 'C', 'sigma'], - ... link_functions={ - ... 'A': 'identity', # A ∈ (-∞, ∞) - ... 'B': 'sigmoid', # B ∈ (0, 1) - ... 'C': 'softplus', # C > 0 - ... 'sigma': 'exp', # σ > 0 - ... }, - ... nll_fn=my_nll, - ... mean_fn=lambda params: params['A'] * (1 - params['B']) / params['C'], - ... ) - >>> - >>> model = NGBoost(distribution=dist, n_trees=100) - >>> model.fit(X, y) - - For Kaggle competitions with custom evaluation metrics, you can define - the NLL to match the competition metric! - """ - - # Available link functions - LINK_FUNCTIONS = { - 'identity': (lambda x: x, lambda x: x), - 'exp': (lambda x: np.exp(np.clip(x, -20, 20)), lambda x: np.log(np.clip(x, 1e-10, None))), - 'softplus': (lambda x: np.log1p(np.exp(np.clip(x, -20, 20))), lambda x: np.where(x > 20, x, np.log(np.expm1(np.minimum(x, 20))))), - 'sigmoid': (lambda x: 1 / (1 + np.exp(-np.clip(x, -20, 20))), lambda x: np.log(np.clip(x / (1 - x + 1e-10), 1e-10, None))), - 'square': (lambda x: x ** 2, lambda x: np.sqrt(np.clip(x, 0, None))), - } - - def __init__( - self, - param_names: list[str], - link_functions: dict[str, str], - nll_fn: callable, - mean_fn: callable | None = None, - variance_fn: callable | None = None, - init_fn: callable | None = None, - use_jax: bool = True, - eps: float = 1e-5, - ): - """Initialize custom distribution. - - Args: - param_names: List of parameter names (e.g., ['A', 'B', 'sigma']) - link_functions: Dict mapping param name to link type: - - 'identity': no transformation, param ∈ (-∞, ∞) - - 'exp': exponential, param > 0 - - 'softplus': log(1 + exp(x)), param > 0 (smoother than exp) - - 'sigmoid': 1/(1+exp(-x)), param ∈ (0, 1) - - 'square': x², param ≥ 0 - nll_fn: Function (y, params_dict) -> array of NLL per sample - mean_fn: Optional function (params_dict) -> mean prediction - variance_fn: Optional function (params_dict) -> variance - init_fn: Optional function (y) -> dict of initial raw param values - use_jax: Try to use JAX for autodiff (falls back to numerical if - unavailable or if the user NLL cannot be traced) - eps: Epsilon for numerical gradients - """ - self._param_names = param_names - self._link_functions = link_functions - self._nll_fn = nll_fn - self._mean_fn = mean_fn - self._variance_fn = variance_fn - self._init_fn = init_fn - self._use_jax = use_jax - self._eps = eps - # Per-sample gradient stack from the last nll_gradient call, used by - # the empirical Fisher approximation in fisher_information() - self._last_grad_stack: NDArray | None = None - - # Check for JAX availability - self._jax_available = False - self._jax_grad_fn = None - self._jax_hess_fn = None - if use_jax: - try: - import jax - import jax.numpy as jnp - self._jax_available = True - self._jax = jax - self._jnp = jnp - # Will compile grad function on first use - except ImportError: - pass - - @property - def n_params(self) -> int: - return len(self._param_names) - - @property - def param_names(self) -> list[str]: - return self._param_names - - def link(self, param_name: str, raw: NDArray) -> NDArray: - link_type = self._link_functions.get(param_name, 'identity') - link_fn, _ = self.LINK_FUNCTIONS[link_type] - return link_fn(raw) - - def link_inv(self, param_name: str, param: NDArray) -> NDArray: - link_type = self._link_functions.get(param_name, 'identity') - _, inv_fn = self.LINK_FUNCTIONS[link_type] - return inv_fn(param) - - def init_params(self, y: NDArray) -> dict[str, float]: - if self._init_fn is not None: - return self._init_fn(y) - - # Default initialization: zeros in raw space - return {name: 0.0 for name in self._param_names} - - def _compute_nll_value(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - """Compute NLL using user-provided function.""" - return self._nll_fn(y, params) - - def _link_derivative(self, param_name: str, constrained: NDArray) -> NDArray: - """Compute d(constrained)/d(raw) for the link function. - - For exp link: d(exp(raw))/d(raw) = exp(raw) = constrained - For identity link: derivative is 1 - For softplus link: d(log(1+exp(raw)))/d(raw) = sigmoid(raw) = 1 - exp(-constrained) - For sigmoid link: d(sigmoid(raw))/d(raw) = constrained * (1 - constrained) - For square link: d(raw^2)/d(raw) = 2*raw = 2*sqrt(constrained) - """ - link_type = self._link_functions.get(param_name, 'identity') - if link_type == 'exp': - return constrained - elif link_type == 'identity': - return np.ones_like(constrained) - elif link_type == 'softplus': - return 1.0 - np.exp(-constrained) - elif link_type == 'sigmoid': - return constrained * (1.0 - constrained) - elif link_type == 'square': - return 2.0 * np.sqrt(np.maximum(constrained, 0.0)) - else: - return np.ones_like(constrained) - - def _link_second_derivative(self, param_name: str, constrained: NDArray) -> NDArray: - """Compute d²(constrained)/d(raw)² for the link function. - - For exp link: d²(exp(raw))/d(raw)² = exp(raw) = constrained - For identity link: 0 - For softplus link: sigmoid'(raw) = s*(1-s) with s = 1 - exp(-constrained) - For sigmoid link: c*(1-c)*(1-2c) with c = constrained - For square link: 2 - """ - link_type = self._link_functions.get(param_name, 'identity') - if link_type == 'exp': - return constrained - elif link_type == 'identity': - return np.zeros_like(constrained) - elif link_type == 'softplus': - s = 1.0 - np.exp(-constrained) - return s * (1.0 - s) - elif link_type == 'sigmoid': - return constrained * (1.0 - constrained) * (1.0 - 2.0 * constrained) - elif link_type == 'square': - return 2.0 * np.ones_like(constrained) - else: - return np.zeros_like(constrained) - - def _numerical_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Compute gradients numerically (vectorized finite differences). - - Perturbs constrained parameters and then applies the chain rule - through the link function to obtain gradients/hessians w.r.t. raw - parameters. - """ - results = {} - eps = self._eps - - # Compute center NLL once - nll_center = self._nll_fn(y, params) - - for param_name in self._param_names: - # Vectorized perturbation in constrained space - params_plus = {k: v.copy() for k, v in params.items()} - params_minus = {k: v.copy() for k, v in params.items()} - - params_plus[param_name] = params[param_name] + eps - params_minus[param_name] = params[param_name] - eps - - nll_plus = self._nll_fn(y, params_plus) - nll_minus = self._nll_fn(y, params_minus) - - # Central difference for gradient w.r.t. constrained params - grad_constrained = (nll_plus - nll_minus) / (2 * eps) - - # Central difference for hessian w.r.t. constrained params - hess_constrained = (nll_plus - 2 * nll_center + nll_minus) / (eps ** 2) - - # Chain rule through the link to get derivatives w.r.t. raw params: - # grad_raw = grad_c * link' - # hess_raw = hess_c * (link')² + grad_c * link'' (full second order) - link_deriv = self._link_derivative(param_name, params[param_name]) - link_second = self._link_second_derivative(param_name, params[param_name]) - grad = grad_constrained * link_deriv - hess = hess_constrained * link_deriv ** 2 + grad_constrained * link_second - - # Ensure positive hessian and clip extremes - hess = np.clip(hess, 1e-6, 1e6) - grad = np.clip(grad, -1e6, 1e6) - - results[param_name] = (grad.astype(np.float32), hess.astype(np.float32)) - - return results - - def _jax_link_fn(self, param_name: str): - """JAX-traceable link function (mirrors LINK_FUNCTIONS).""" - jnp = self._jnp - link_type = self._link_functions.get(param_name, 'identity') - if link_type == 'exp': - return lambda x: jnp.exp(jnp.clip(x, -20, 20)) - elif link_type == 'softplus': - return lambda x: jnp.log1p(jnp.exp(jnp.clip(x, -20, 20))) - elif link_type == 'sigmoid': - return lambda x: 1.0 / (1.0 + jnp.exp(-jnp.clip(x, -20, 20))) - elif link_type == 'square': - return lambda x: x ** 2 - return lambda x: x - - def _jax_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Compute gradients w.r.t. RAW parameters using JAX autodiff. - - The trainer keeps predictions in raw (link) space, so gradients must - be d(NLL)/d(raw). This differentiates the composition - nll(y, link(raw)) directly, which also yields the exact raw-space - Hessian (including the grad * link'' second-order term). - """ - jax = self._jax - jnp = self._jnp - - # Create grad and hessian functions once (cached pattern) - if self._jax_grad_fn is None: - link_fns = [self._jax_link_fn(name) for name in self._param_names] - param_names = self._param_names - - def single_nll_raw(raw_values, y_single): - params_dict = { - name: jnp.reshape(link_fns[j](raw_values[j]), (1,)) - for j, name in enumerate(param_names) - } - return self._nll_fn(jnp.reshape(y_single, (1,)), params_dict)[0] - - self._jax_grad_fn = jax.grad(single_nll_raw) - self._jax_hess_fn = jax.hessian(single_nll_raw) - - grad_fn = self._jax_grad_fn - hess_fn = self._jax_hess_fn - - # Vectorize over samples using vmap - batched_grad_fn = jax.vmap(grad_fn, in_axes=(0, 0)) - batched_hess_fn = jax.vmap(hess_fn, in_axes=(0, 0)) - - # Recover raw values from the constrained params (links are monotone) - raw_values = jnp.stack( - [jnp.array(self.link_inv(name, np.asarray(params[name]))) - for name in self._param_names], - axis=-1, - ) - y_jax = jnp.array(y) - - grads = batched_grad_fn(raw_values, y_jax) # shape (n, n_params) - hess_matrices = batched_hess_fn(raw_values, y_jax) # shape (n, n_params, n_params) - - results = {} - for j, name in enumerate(self._param_names): - g = np.array(grads[:, j], dtype=np.float32) - h = np.maximum(np.array(hess_matrices[:, j, j], dtype=np.float32), 1e-6) - results[name] = (g, h) - - return results - - def nll_gradient( - self, - y: NDArray, - params: dict[str, NDArray], - ) -> dict[str, GradHess]: - """Compute gradients w.r.t. raw parameters (auto-selects JAX or numerical).""" - results = None - if self._jax_available: - try: - results = self._jax_gradient(y, params) - except Exception as exc: - # A user function written with numpy instead of jax.numpy is - # the most common reason tracing fails. Disable JAX for this - # distribution instance so every boosting round does not pay - # for the same failed trace, then use the documented numerical - # path. Never manufacture gradients after an autodiff error. - self._jax_available = False - self._jax_grad_fn = None - self._jax_hess_fn = None - warnings.warn( - "JAX autodiff failed for the custom distribution; " - "falling back to numerical differentiation for this instance. " - "Use jax.numpy operations in nll_fn to keep autodiff enabled " - f"({type(exc).__name__}: {exc})", - RuntimeWarning, - stacklevel=2, - ) - - if results is None: - results = self._numerical_gradient(y, params) - - # Cache per-sample gradients for the empirical Fisher approximation - self._last_grad_stack = np.stack( - [results[name][0] for name in self._param_names], axis=1 - ) - return results - - def fisher_information( - self, - params: dict[str, NDArray], - y: NDArray | None = None, - ) -> NDArray: - """Empirical Fisher information (diagonal approximation). - - The exact Fisher matrix is unknown for user-defined likelihoods, so - this uses the empirical (outer-product) approximation restricted to - its diagonal: F[j, j] = E_i[g_i[j]²], the batch mean of squared - per-sample NLL gradients (JAX autodiff or numerical). The same - diagonal is used for every sample, and a floor keeps the matrix - invertible when gradients vanish. - - Gradients come from `y` when provided; otherwise from the most - recent nll_gradient call (the natural_gradient code path computes - gradients immediately before calling this). Falls back to identity - when no gradient information is available. - """ - n_samples = next(iter(params.values())).shape[0] - n_params = self.n_params - - grad_stack = None - if y is not None: - grads = self.nll_gradient(y, params) - grad_stack = np.stack( - [grads[name][0] for name in self._param_names], axis=1 - ) - elif ( - self._last_grad_stack is not None - and self._last_grad_stack.shape[0] == n_samples - ): - grad_stack = self._last_grad_stack - - F = np.zeros((n_samples, n_params, n_params), dtype=np.float32) - if grad_stack is None: - # No gradient information: identity (plain gradient descent) - for i in range(n_params): - F[:, i, i] = 1.0 - return F - - diag = np.maximum( - np.mean(grad_stack.astype(np.float64) ** 2, axis=0), 1e-6 - ) - for i in range(n_params): - F[:, i, i] = diag[i] - - return F - - def mean(self, params: dict[str, NDArray]) -> NDArray: - if self._mean_fn is not None: - return self._mean_fn(params) - # Default: first parameter - return params[self._param_names[0]] - - def variance(self, params: dict[str, NDArray]) -> NDArray: - if self._variance_fn is not None: - return self._variance_fn(params) - # Default: ones - return np.ones_like(list(params.values())[0]) - - def quantile(self, params: dict[str, NDArray], q: float) -> NDArray: - """Approximate quantile using Normal assumption.""" - from scipy import stats - mean = self.mean(params) - std = np.sqrt(self.variance(params)) - return stats.norm.ppf(q, loc=mean, scale=std) - - def sample( - self, - params: dict[str, NDArray], - n_samples: int = 1, - seed: int | None = None, - ) -> NDArray: - """Sample using Normal approximation.""" - rng = np.random.default_rng(seed) - mean = self.mean(params) - std = np.sqrt(self.variance(params)) - n_obs = mean.shape[0] - return rng.normal(mean[:, None], std[:, None], size=(n_obs, n_samples)) - - def nll(self, y: NDArray, params: dict[str, NDArray]) -> NDArray: - return self._nll_fn(y, params) - - -def create_custom_distribution( - param_names: list[str], - link_functions: dict[str, str], - nll_fn: callable, - mean_fn: callable | None = None, - variance_fn: callable | None = None, -) -> CustomDistribution: - """Convenience function to create a custom distribution. - - Example: Model y ~ Normal(A * exp(-B*x_feature), sigma) - - >>> dist = create_custom_distribution( - ... param_names=['A', 'B', 'sigma'], - ... link_functions={'A': 'exp', 'B': 'softplus', 'sigma': 'exp'}, - ... nll_fn=lambda y, p: 0.5*np.log(2*np.pi*p['sigma']**2) + (y-p['A'])**2/(2*p['sigma']**2), - ... mean_fn=lambda p: p['A'], - ... variance_fn=lambda p: p['sigma']**2, - ... ) - """ - return CustomDistribution( - param_names=param_names, - link_functions=link_functions, - nll_fn=nll_fn, - mean_fn=mean_fn, - variance_fn=variance_fn, - ) - - -# ============================================================================= -# Distribution Registry -# ============================================================================= - -DISTRIBUTIONS: dict[str, type[Distribution]] = { - 'normal': Normal, - 'gaussian': Normal, - 'lognormal': LogNormal, - 'log_normal': LogNormal, - 'gamma': Gamma, - 'poisson': Poisson, - 'studentt': StudentT, - 'student_t': StudentT, - 't': StudentT, - # Kaggle competition favorites - 'tweedie': Tweedie, - 'negativebinomial': NegativeBinomial, - 'negative_binomial': NegativeBinomial, - 'negbin': NegativeBinomial, -} - - -def get_distribution(name: str | Distribution) -> Distribution: - """Get distribution by name or return instance. - - Args: - name: Distribution name or Distribution instance - - Returns: - Distribution instance - - Example: - >>> dist = get_distribution('normal') - >>> dist = get_distribution('gamma') - """ - if isinstance(name, Distribution): - return name - - name_lower = name.lower() - if name_lower not in DISTRIBUTIONS: - available = ', '.join(sorted(set(DISTRIBUTIONS.keys()))) - raise ValueError(f"Unknown distribution '{name}'. Available: {available}") - - return DISTRIBUTIONS[name_lower]() - - -def register_distribution( - name: str, - cls: type[Distribution], - *, - override: bool = False, -) -> type[Distribution]: - """Register a custom Distribution class under a string name. - - After registration the name works everywhere a built-in distribution - name does, e.g. ``NaturalBoost(distribution='mydist')``. - - Args: - name: Name to register the distribution under. Lookup is - case-insensitive (the name is stored lowercased, matching - ``get_distribution``). - cls: A ``Distribution`` subclass. It is instantiated with no - arguments each time the name is resolved. - override: Pass True to replace an existing registration (including - a built-in name). Without it a duplicate name raises ValueError. - - Returns: - ``cls`` unchanged. - - Example: - >>> class MyNormal(Normal): - ... pass - >>> register_distribution('mynormal', MyNormal) - >>> model = NaturalBoost(distribution='mynormal') - """ - if not isinstance(name, str) or not name: - raise TypeError(f"Distribution name must be a non-empty string, got {name!r}") - if not (isinstance(cls, type) and issubclass(cls, Distribution)): - raise TypeError( - f"Distribution must be a Distribution subclass, got {cls!r}" - ) - key = name.lower() - if not override and key in DISTRIBUTIONS: - raise ValueError( - f"Distribution '{key}' is already registered. " - "Pass override=True to replace it." - ) - DISTRIBUTIONS[key] = cls - return cls - - -def list_distributions() -> list[str]: - """List available distribution names.""" - return sorted(set(DISTRIBUTIONS.keys())) diff --git a/src/openboost/_importance.py b/src/openboost/_importance.py deleted file mode 100644 index 39c4e68..0000000 --- a/src/openboost/_importance.py +++ /dev/null @@ -1,304 +0,0 @@ -"""Feature importance utilities for OpenBoost. - -Phase 13: Compute feature importances from any tree-based model. - -This module provides functions to compute feature importances that work -with any model containing trees: GradientBoosting, DART, MultiClass, -OpenBoostGAM, etc. - -Example: - >>> import openboost as ob - >>> from openboost import compute_feature_importances - >>> - >>> model = ob.GradientBoosting(n_trees=100).fit(X, y) - >>> importances = compute_feature_importances(model) - >>> - >>> # Top features - >>> top_features = np.argsort(importances)[::-1][:10] - >>> for i in top_features: - ... print(f"Feature {i}: {importances[i]:.4f}") -""" - -from __future__ import annotations - -import warnings -from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable - -import numpy as np - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -@runtime_checkable -class HasTrees(Protocol): - """Protocol for models with trees (duck typing). - - Any model with a `trees_` attribute containing TreeStructure objects - can use the importance functions. - """ - trees_: list - - -def compute_feature_importances( - model: Any, - importance_type: str = 'frequency', - normalize: bool = True, -) -> NDArray: - """Compute feature importances from any tree-based model. - - Works with: GradientBoosting, DART, OpenBoostGAM, MultiClassGradientBoosting, - and any model with a `trees_` attribute. - - Args: - model: A fitted model with `trees_` attribute. - importance_type: Type of importance calculation: - - 'frequency': Number of times feature is used for splits (default) - - 'gain': Sum of gain from splits on each feature (if available) - - 'cover': Sum of samples covered by splits (if available) - normalize: If True, normalize importances to sum to 1. - - Returns: - importances: Array of shape (n_features,) with importance scores. - - Raises: - ValueError: If model has no trees or unknown importance_type. - - Example: - >>> model = ob.GradientBoosting(n_trees=100).fit(X, y) - >>> importances = compute_feature_importances(model) - >>> - >>> # Use with sklearn-style attribute - >>> model.feature_importances_ = compute_feature_importances(model) - """ - if not hasattr(model, 'trees_'): - raise ValueError("Model must have 'trees_' attribute") - - trees = _get_trees_flat(model) - - if not trees: - raise ValueError("Model has no fitted trees") - - # Get number of features - n_features = _get_n_features(model, trees) - - # Compute importances - importances = np.zeros(n_features, dtype=np.float64) - - for tree in trees: - _accumulate_importance(tree, importances, importance_type) - - # Normalize if requested - if normalize: - total = importances.sum() - if total > 0: - importances /= total - - return importances.astype(np.float32) - - -def _get_trees_flat(model: Any) -> list: - """Extract a flat list of trees from various model types. - - Handles: - - GradientBoosting, DART: trees_ is list of TreeStructure - - MultiClassGradientBoosting: trees_ is list of lists (one per class per round) - - OpenBoostGAM: trees_ is dict of feature -> list of trees - """ - trees = model.trees_ - - if not trees: - return [] - - # Check if it's a list of lists (MultiClass) - if isinstance(trees, list) and trees and isinstance(trees[0], list): - return [t for round_trees in trees for t in round_trees] - - # Check if it's a dict (GAM) - if isinstance(trees, dict): - return [t for feature_trees in trees.values() for t in feature_trees] - - # Regular flat list - unwrap LinearLeafTree if needed - result = [] - for tree in trees: - if hasattr(tree, 'tree_structure'): - result.append(tree.tree_structure) - else: - result.append(tree) - return result - - -def _get_n_features(model: Any, trees: list) -> int: - """Get number of features from model or trees.""" - # Try model attributes first - if hasattr(model, 'n_features_in_'): - return model.n_features_in_ - - if hasattr(model, 'X_binned_') and model.X_binned_ is not None: - return model.X_binned_.n_features - - # Infer from trees - if trees: - return trees[0].n_features - - raise ValueError("Cannot determine number of features") - - -def _accumulate_importance(tree, importances: NDArray, importance_type: str) -> None: - """Accumulate importance scores from a single tree. - - Args: - tree: TreeStructure object - importances: Array to accumulate into (modified in place) - importance_type: 'frequency', 'gain', or 'cover' - """ - n_nodes = tree.n_nodes - features = tree.features - left_children = tree.left_children - - for node_idx in range(n_nodes): - # Check if this is a split node (not a leaf) - if left_children[node_idx] != -1: - feature = features[node_idx] - - if feature < 0 or feature >= len(importances): - continue - - if importance_type == 'frequency': - # Count splits - importances[feature] += 1.0 - - elif importance_type == 'gain': - # Use gain if stored, otherwise fall back to frequency - if hasattr(tree, 'split_gains') and tree.split_gains is not None: - importances[feature] += tree.split_gains[node_idx] - else: - # Fallback: use 1.0 (equivalent to frequency) - warnings.warn( - f"Tree does not have {importance_type} data, " - "falling back to frequency-based importance", - UserWarning, - stacklevel=2, - ) - importances[feature] += 1.0 - - elif importance_type == 'cover': - # Use sample counts if stored, otherwise fall back to frequency - if hasattr(tree, 'node_counts') and tree.node_counts is not None: - importances[feature] += tree.node_counts[node_idx] - else: - # Fallback: use 1.0 - warnings.warn( - f"Tree does not have {importance_type} data, " - "falling back to frequency-based importance", - UserWarning, - stacklevel=2, - ) - importances[feature] += 1.0 - - else: - raise ValueError(f"Unknown importance_type: '{importance_type}'. " - "Use 'frequency', 'gain', or 'cover'.") - - -def get_feature_importance_dict( - model: Any, - feature_names: list[str] | None = None, - importance_type: str = 'frequency', - top_n: int | None = None, -) -> dict[str, float]: - """Get feature importances as a sorted dictionary. - - Convenience function that returns importances as a dict, optionally - with feature names and limited to top N features. - - Args: - model: Fitted tree-based model. - feature_names: Optional list of feature names. - importance_type: Type of importance ('frequency', 'gain', 'cover'). - top_n: If provided, return only top N features. - - Returns: - Dict mapping feature name/index to importance, sorted by importance. - - Example: - >>> importance_dict = get_feature_importance_dict( - ... model, - ... feature_names=['age', 'income', 'score'], - ... top_n=2 - ... ) - >>> # {'income': 0.45, 'age': 0.32} - """ - importances = compute_feature_importances(model, importance_type, normalize=True) - - # Create feature names if not provided - if feature_names is None: - feature_names = [f"feature_{i}" for i in range(len(importances))] - - # Create dict and sort by importance - importance_dict = dict(zip(feature_names, importances, strict=False)) - sorted_dict = dict(sorted(importance_dict.items(), key=lambda x: x[1], reverse=True)) - - # Limit to top N if requested - if top_n is not None: - sorted_dict = dict(list(sorted_dict.items())[:top_n]) - - return sorted_dict - - -def plot_feature_importances( - model: Any, - feature_names: list[str] | None = None, - importance_type: str = 'frequency', - top_n: int = 20, - ax=None, - **kwargs, -): - """Plot feature importances as a horizontal bar chart. - - Args: - model: Fitted tree-based model. - feature_names: Optional list of feature names. - importance_type: Type of importance ('frequency', 'gain', 'cover'). - top_n: Number of top features to show. - ax: Matplotlib axes to plot on (creates new if None). - **kwargs: Additional arguments passed to barh(). - - Returns: - Matplotlib axes object. - - Example: - >>> plot_feature_importances(model, top_n=10) - >>> plt.show() - """ - try: - import matplotlib.pyplot as plt - except ImportError as err: - raise ImportError("matplotlib is required for plotting. Install with: pip install matplotlib") from err - - importances = compute_feature_importances(model, importance_type, normalize=True) - - # Get indices of top features - top_indices = np.argsort(importances)[::-1][:top_n] - - # Create feature names if not provided - if feature_names is None: - feature_names = [f"feature_{i}" for i in range(len(importances))] - - # Get names and values for top features - top_names = [feature_names[i] for i in top_indices] - top_values = importances[top_indices] - - # Create plot - if ax is None: - fig, ax = plt.subplots(figsize=(10, max(6, top_n * 0.3))) - - y_pos = np.arange(len(top_names)) - ax.barh(y_pos, top_values, **kwargs) - ax.set_yticks(y_pos) - ax.set_yticklabels(top_names) - ax.invert_yaxis() # Top feature at top - ax.set_xlabel(f'Importance ({importance_type})') - ax.set_title('Feature Importances') - - return ax diff --git a/src/openboost/_loss.py b/src/openboost/_loss.py deleted file mode 100644 index 58343d8..0000000 --- a/src/openboost/_loss.py +++ /dev/null @@ -1,1124 +0,0 @@ -"""Loss functions and GPU gradient computation for OpenBoost. - -Provides efficient GPU kernels for computing gradients and hessians -of common loss functions, enabling fully batched training. -""" - -from __future__ import annotations - -import warnings -from collections.abc import Callable -from typing import TYPE_CHECKING - -import numpy as np - -from ._backends import is_cuda - -if TYPE_CHECKING: - from numpy.typing import NDArray - -# Type alias for loss functions -LossFunction = Callable[[np.ndarray, np.ndarray], tuple[np.ndarray, np.ndarray]] - -# ============================================================================= -# Loss Registry (Extensibility) -# ============================================================================= - -# name -> gradient fn, or a factory marked with ``__openboost_factory__ = True`` -# for parameterized losses (called as ``factory(**kwargs)`` to build the fn). -# Seeded with the built-in losses at the bottom of this module; extended at -# runtime via ``register_loss``. -_LOSS_REGISTRY: dict[str, LossFunction] = {} - -# name -> true scalar loss fn registered via ``register_loss(loss_value_fn=...)`` -_LOSS_VALUE_REGISTRY: dict[str, Callable[[np.ndarray, np.ndarray], float]] = {} - -# Snapshot of the built-in seed, used to detect overridden built-ins. -_BUILTIN_LOSS_SEED: dict[str, LossFunction] = {} - - -def register_loss( - name: str, - fn: LossFunction, - *, - loss_value_fn: Callable[[np.ndarray, np.ndarray], float] | None = None, - override: bool = False, -) -> LossFunction: - """Register a custom loss function under a string name. - - After registration the name works everywhere a built-in loss name does, - e.g. ``GradientBoosting(loss='myloss')``. - - Args: - name: Name to register the loss under. - fn: Loss callable with signature ``fn(pred, y) -> (grad, hess)``. - loss_value_fn: Optional callable ``(pred, y) -> float`` returning the - TRUE scalar loss value. When provided, training history and early - stopping report this value instead of the second-order Taylor - proxy ``mean(grad^2 / (2*hess))``. - override: Pass True to replace an existing registration (including - shadowing a built-in). Without it a duplicate name raises - ValueError. - - Returns: - ``fn`` unchanged. - - Precedence for the reported train/val loss value of a loss: - 1. ``loss_value_fn`` registered here for the name. - 2. A ``loss_value`` attribute on the loss callable - (``fn.loss_value = lambda pred, y: ...``). - 3. The built-in formula (built-in names only). - 4. The second-order Taylor proxy ``mean(grad^2 / (2*hess))``. - - Example: - >>> def my_mse(pred, y): - ... return (pred - y).astype(np.float32), np.ones_like(pred, dtype=np.float32) - >>> register_loss('my_mse', my_mse, - ... loss_value_fn=lambda pred, y: float(np.mean((pred - y) ** 2))) - >>> model = GradientBoosting(loss='my_mse') - """ - if not isinstance(name, str) or not name: - raise TypeError(f"Loss name must be a non-empty string, got {name!r}") - if not callable(fn): - raise TypeError(f"Loss function must be callable, got {type(fn).__name__}") - if loss_value_fn is not None and not callable(loss_value_fn): - raise TypeError( - f"loss_value_fn must be callable, got {type(loss_value_fn).__name__}" - ) - if not override and name in _LOSS_REGISTRY: - raise ValueError( - f"Loss '{name}' is already registered. Pass override=True to replace it." - ) - _LOSS_REGISTRY[name] = fn - if loss_value_fn is not None: - _LOSS_VALUE_REGISTRY[name] = loss_value_fn - else: - # Don't leave a stale value fn behind when overriding. - _LOSS_VALUE_REGISTRY.pop(name, None) - return fn - - -def is_builtin_loss(loss) -> bool: - """True if *loss* names a built-in loss that has not been overridden.""" - if not isinstance(loss, str): - return False - entry = _LOSS_REGISTRY.get(loss) - return entry is not None and _BUILTIN_LOSS_SEED.get(loss) is entry - - -def device_loss(fn: LossFunction) -> LossFunction: - """Mark a custom loss as device-native (opt-in GPU contract). - - On the CUDA backend an unmarked custom loss triggers a host round-trip - every boosting round: predictions are copied to the host, the callable is - invoked with numpy arrays, and the returned (grad, hess) are copied back - to the device. - - A callable decorated with ``@openboost.device_loss`` instead receives the - DEVICE prediction array and the device-resident targets as-is (targets are - moved to the device once per fit and cached), and MUST return device - (grad, hess) arrays of dtype float32 with the same length as ``pred``. - - On the CPU backend the marker is a no-op: the callable simply receives - numpy arrays like any other custom loss. - - Example: - >>> @openboost.device_loss - ... def my_gpu_mse(pred_dev, y_dev): - ... # pred_dev / y_dev are device arrays; return device arrays. - ... ... - """ - if not callable(fn): - raise TypeError(f"device_loss expects a callable, got {type(fn).__name__}") - fn.__openboost_device__ = True - return fn - - -def get_loss_function(loss: str | LossFunction, **kwargs) -> LossFunction: - """Get a loss function by name or return custom callable. - - Args: - loss: Loss function name or callable. Available: - - 'mse': Mean Squared Error (regression) - - 'mae': Mean Absolute Error (L1 regression) - - 'huber': Huber loss (robust regression) - - 'logloss': Binary cross-entropy (classification) - - 'quantile': Quantile regression (percentile prediction) - - 'poisson': Poisson deviance (count data) - - 'gamma': Gamma deviance (positive continuous) - - 'tweedie': Tweedie deviance (compound Poisson-Gamma) - - any name registered via ``register_loss`` - **kwargs: Additional parameters for specific losses: - - quantile_alpha: Quantile level for 'quantile' loss (default 0.5) - - tweedie_rho: Variance power for 'tweedie' loss (default 1.5) - - Returns: - Loss function callable. - - Examples: - >>> loss_fn = get_loss_function('mse') - >>> loss_fn = get_loss_function('quantile', quantile_alpha=0.9) - >>> loss_fn = get_loss_function('tweedie', tweedie_rho=1.5) - """ - if callable(loss): - return loss - - entry = _LOSS_REGISTRY.get(loss) - if entry is None: - available = ', '.join(sorted(_LOSS_REGISTRY)) - raise ValueError(f"Unknown loss '{loss}'. Available: {available}") - - # Parameterized built-ins are stored as factories that close over kwargs. - if getattr(entry, '__openboost_factory__', False): - return entry(**kwargs) - return entry - - -def compute_loss_value(loss_name: str, pred: np.ndarray, y: np.ndarray, **kwargs) -> float: - """Compute the actual scalar loss value (not the grad/hess proxy). - - For built-in objectives this uses the true loss formula. For custom - (registered) losses, precedence is: - - 1. ``loss_value_fn`` passed to :func:`register_loss` for this name. - 2. A ``loss_value`` attribute on the registered gradient callable - (``fn.loss_value = lambda pred, y: ...``). - 3. The second-order Taylor approximation ``mean(grad^2 / (2 * hess))``. - """ - pred = np.asarray(pred, dtype=np.float64) - y = np.asarray(y, dtype=np.float64) - - # Custom true-loss hooks take precedence (also covers overridden built-ins). - value_fn = _LOSS_VALUE_REGISTRY.get(loss_name) - if value_fn is None: - entry = _LOSS_REGISTRY.get(loss_name) - if entry is not None: - value_fn = getattr(entry, 'loss_value', None) - if value_fn is not None: - return float(value_fn(pred, y)) - - if not is_builtin_loss(loss_name): - # Unknown or custom loss without a value hook: grad/hess proxy. - return _grad_hess_proxy(loss_name, pred, y, **kwargs) - - if loss_name == 'mse' or loss_name == 'squared_error': - return float(np.mean((pred - y) ** 2)) - - if loss_name == 'mae' or loss_name == 'l1' or loss_name == 'absolute_error': - return float(np.mean(np.abs(pred - y))) - - if loss_name == 'huber': - delta = kwargs.get('huber_delta', 1.0) - diff = np.abs(pred - y) - loss = np.where(diff <= delta, 0.5 * diff ** 2, delta * (diff - 0.5 * delta)) - return float(np.mean(loss)) - - if loss_name == 'quantile': - alpha = kwargs.get('quantile_alpha', 0.5) - residual = y - pred - return float(np.mean(np.where(residual >= 0, alpha * residual, (alpha - 1) * residual))) - - if loss_name == 'logloss' or loss_name == 'binary_crossentropy': - p = 1.0 / (1.0 + np.exp(-np.clip(pred, -500, 500))) - p = np.clip(p, 1e-15, 1 - 1e-15) - return float(-np.mean(y * np.log(p) + (1 - y) * np.log(1 - p))) - - if loss_name == 'poisson': - return float(np.mean(np.exp(np.clip(pred, -20, 20)) - y * pred)) - - if loss_name == 'gamma': - return float(np.mean(pred + y * np.exp(-np.clip(pred, -20, 20)))) - - if loss_name == 'tweedie': - rho = kwargs.get('tweedie_rho', 1.5) - mu = np.exp(np.clip(pred, -20, 20)) - return float(np.mean(-y * mu ** (1 - rho) / (1 - rho) + mu ** (2 - rho) / (2 - rho))) - - # Unknown/custom loss: fall back to grad/hess proxy - return _grad_hess_proxy(loss_name, pred, y, **kwargs) - - -def _grad_hess_proxy(loss_name, pred, y, **kwargs): - """Fallback: second-order Taylor approximation ``mean(grad^2 / (2*hess))``.""" - loss_fn = get_loss_function(loss_name, **kwargs) - grad, hess = loss_fn(np.asarray(pred, dtype=np.float32), - np.asarray(y, dtype=np.float32)) - if hasattr(grad, 'copy_to_host'): - grad = grad.copy_to_host() - if hasattr(hess, 'copy_to_host'): - hess = hess.copy_to_host() - grad = np.asarray(grad, dtype=np.float64) - hess = np.maximum(np.asarray(hess, dtype=np.float64), 1e-10) - return float(np.mean(grad ** 2 / (2.0 * hess))) - - -# ============================================================================= -# MSE Loss (Regression) -# ============================================================================= - -def mse_gradient(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """Compute MSE gradient and hessian. - - Loss: L = 0.5 * (pred - y)^2 - Gradient: dL/dpred = (pred - y) - Hessian: d²L/dpred² = 1 - - Uses the 0.5 * MSE convention (matching XGBoost) so that - reg_lambda has equivalent effect across libraries. - """ - if is_cuda(): - return _mse_gradient_gpu(pred, y) - return _mse_gradient_cpu(pred, y) - - -def _mse_gradient_cpu(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """CPU implementation of MSE gradient.""" - grad = (pred - y).astype(np.float32) - hess = np.full_like(pred, 1.0, dtype=np.float32) - return grad, hess - - -def _mse_gradient_gpu(pred, y): - """GPU implementation of MSE gradient.""" - from numba import cuda - _ensure_mse_kernel() - - # Handle device arrays - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _mse_gradient_kernel[blocks, threads](pred, y, grad, hess, n) - - return grad, hess - - -def _get_mse_kernel(): - """Lazily compile MSE gradient kernel.""" - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n): - idx = cuda.grid(1) - if idx < n: - grad[idx] = pred[idx] - y[idx] - hess[idx] = 1.0 - - return kernel - - -_mse_gradient_kernel = None - - -def _ensure_mse_kernel(): - global _mse_gradient_kernel - if _mse_gradient_kernel is None: - _mse_gradient_kernel = _get_mse_kernel() - return _mse_gradient_kernel - - -# Eager initialization on module load if CUDA available -if is_cuda(): - try: - _mse_gradient_kernel = _get_mse_kernel() - except Exception: - warnings.warn("Failed to compile MSE CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# LogLoss (Binary Classification) -# ============================================================================= - -def logloss_gradient(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """Compute LogLoss gradient and hessian. - - Loss: L = -y*log(p) - (1-y)*log(1-p), where p = sigmoid(pred) - Gradient: dL/dpred = p - y - Hessian: d²L/dpred² = p * (1 - p) - """ - if is_cuda(): - return _logloss_gradient_gpu(pred, y) - return _logloss_gradient_cpu(pred, y) - - -def _sigmoid(x: NDArray) -> NDArray: - """Numerically stable sigmoid.""" - return np.where(x >= 0, - 1 / (1 + np.exp(-x)), - np.exp(x) / (1 + np.exp(x))) - - -def _logloss_gradient_cpu(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """CPU implementation of LogLoss gradient.""" - p = _sigmoid(pred) - grad = (p - y).astype(np.float32) - hess = (p * (1 - p)).astype(np.float32) - # Clip hessian to avoid numerical issues - hess = np.clip(hess, 1e-6, 1.0 - 1e-6) - return grad, hess - - -def _ensure_logloss_kernel(): - global _logloss_gradient_kernel - if _logloss_gradient_kernel is None: - _logloss_gradient_kernel = _get_logloss_kernel() - return _logloss_gradient_kernel - - -def _logloss_gradient_gpu(pred, y): - """GPU implementation of LogLoss gradient.""" - from numba import cuda - _ensure_logloss_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _logloss_gradient_kernel[blocks, threads](pred, y, grad, hess, n) - - return grad, hess - - -def _get_logloss_kernel(): - """Lazily compile LogLoss gradient kernel.""" - import math - - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n): - idx = cuda.grid(1) - if idx < n: - # Numerically stable sigmoid - x = pred[idx] - if x >= 0: - p = 1.0 / (1.0 + math.exp(-x)) - else: - exp_x = math.exp(x) - p = exp_x / (1.0 + exp_x) - - grad[idx] = p - y[idx] - h = p * (1.0 - p) - # Clip hessian - hess[idx] = max(1e-6, min(h, 1.0 - 1e-6)) - - return kernel - - -_logloss_gradient_kernel = None - -if is_cuda(): - try: - _logloss_gradient_kernel = _get_logloss_kernel() - except Exception: - warnings.warn("Failed to compile LogLoss CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Huber Loss (Robust Regression) -# ============================================================================= - -def huber_gradient(pred: NDArray, y: NDArray, delta: float = 1.0) -> tuple[NDArray, NDArray]: - """Compute Huber loss gradient and hessian. - - Loss: L = 0.5 * (pred - y)^2 if |pred - y| <= delta - delta * |pred - y| - 0.5 * delta^2 otherwise - """ - if is_cuda(): - return _huber_gradient_gpu(pred, y, delta) - return _huber_gradient_cpu(pred, y, delta) - - -def _huber_gradient_cpu(pred: NDArray, y: NDArray, delta: float = 1.0) -> tuple[NDArray, NDArray]: - """CPU implementation of Huber gradient.""" - diff = pred - y - abs_diff = np.abs(diff) - - # Gradient - grad = np.where(abs_diff <= delta, diff, delta * np.sign(diff)) - - # Hessian (second derivative) - hess = np.where(abs_diff <= delta, 1.0, 0.0) - # Add small constant for stability - hess = np.maximum(hess, 1e-6) - - return grad.astype(np.float32), hess.astype(np.float32) - - -def _ensure_huber_kernel(): - global _huber_gradient_kernel - if _huber_gradient_kernel is None: - _huber_gradient_kernel = _get_huber_kernel() - return _huber_gradient_kernel - - -def _huber_gradient_gpu(pred, y, delta: float = 1.0): - """GPU implementation of Huber gradient.""" - from numba import cuda - _ensure_huber_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _huber_gradient_kernel[blocks, threads](pred, y, grad, hess, n, delta) - - return grad, hess - - -def _get_huber_kernel(): - """Lazily compile Huber gradient kernel.""" - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n, delta): - idx = cuda.grid(1) - if idx < n: - diff = pred[idx] - y[idx] - abs_diff = abs(diff) - - if abs_diff <= delta: - grad[idx] = diff - hess[idx] = 1.0 - else: - if diff > 0: - grad[idx] = delta - else: - grad[idx] = -delta - hess[idx] = 1e-6 # Small constant for stability - - return kernel - - -_huber_gradient_kernel = None - -if is_cuda(): - try: - _huber_gradient_kernel = _get_huber_kernel() - except Exception: - warnings.warn("Failed to compile Huber CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# MAE Loss (L1 Regression) - Phase 9.1 -# ============================================================================= - -def mae_gradient(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """Compute MAE (L1) gradient and hessian. - - Loss: L = |pred - y| - Gradient: sign(pred - y) - Hessian: 0 (use small constant for GBDT stability) - - Note: MAE is not twice-differentiable at pred=y, so we use a small - constant hessian. This is the standard approach in XGBoost/LightGBM. - """ - if is_cuda(): - return _mae_gradient_gpu(pred, y) - return _mae_gradient_cpu(pred, y) - - -def _mae_gradient_cpu(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """CPU implementation of MAE gradient.""" - diff = pred - y - grad = np.sign(diff).astype(np.float32) - # Use small constant hessian for stability (standard practice) - hess = np.ones_like(pred, dtype=np.float32) * 1.0 - return grad, hess - - -def _ensure_mae_kernel(): - global _mae_gradient_kernel - if _mae_gradient_kernel is None: - _mae_gradient_kernel = _get_mae_kernel() - return _mae_gradient_kernel - - -def _mae_gradient_gpu(pred, y): - """GPU implementation of MAE gradient.""" - from numba import cuda - _ensure_mae_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _mae_gradient_kernel[blocks, threads](pred, y, grad, hess, n) - - return grad, hess - - -def _get_mae_kernel(): - """Lazily compile MAE gradient kernel.""" - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n): - idx = cuda.grid(1) - if idx < n: - diff = pred[idx] - y[idx] - if diff > 0: - grad[idx] = 1.0 - elif diff < 0: - grad[idx] = -1.0 - else: - grad[idx] = 0.0 - hess[idx] = 1.0 - - return kernel - - -_mae_gradient_kernel = None - -if is_cuda(): - try: - _mae_gradient_kernel = _get_mae_kernel() - except Exception: - warnings.warn("Failed to compile MAE CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Quantile Loss (Pinball Loss) - Phase 9.1 -# ============================================================================= - -def quantile_gradient(pred: NDArray, y: NDArray, alpha: float = 0.5) -> tuple[NDArray, NDArray]: - """Compute Quantile (Pinball) loss gradient and hessian. - - Loss: L = alpha * max(y - pred, 0) + (1 - alpha) * max(pred - y, 0) - - This is the standard quantile regression loss: - - alpha=0.5: Median regression (equivalent to MAE) - - alpha=0.9: 90th percentile - - alpha=0.1: 10th percentile - - Gradient: - alpha - 1 if pred > y (over-prediction) - alpha if pred < y (under-prediction) - - Hessian: Use constant (not twice-differentiable) - - Args: - pred: Predictions - y: Targets - alpha: Quantile level (0 < alpha < 1) - """ - if is_cuda(): - return _quantile_gradient_gpu(pred, y, alpha) - return _quantile_gradient_cpu(pred, y, alpha) - - -def _quantile_gradient_cpu(pred: NDArray, y: NDArray, alpha: float = 0.5) -> tuple[NDArray, NDArray]: - """CPU implementation of Quantile gradient. - - Quantile loss: L = alpha * max(y - pred, 0) + (1 - alpha) * max(pred - y, 0) - - Gradient: - dL/dpred = (1 - alpha) if pred > y (over-prediction) - dL/dpred = -alpha if pred < y (under-prediction) - """ - diff = pred - y - # Gradient: (1 - alpha) if pred > y, -alpha if pred <= y - grad = np.where(diff > 0, 1.0 - alpha, -alpha).astype(np.float32) - # Use constant hessian - hess = np.ones_like(pred, dtype=np.float32) - return grad, hess - - -def _ensure_quantile_kernel(): - global _quantile_gradient_kernel - if _quantile_gradient_kernel is None: - _quantile_gradient_kernel = _get_quantile_kernel() - return _quantile_gradient_kernel - - -def _quantile_gradient_gpu(pred, y, alpha: float = 0.5): - """GPU implementation of Quantile gradient.""" - from numba import cuda - _ensure_quantile_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _quantile_gradient_kernel[blocks, threads](pred, y, grad, hess, n, alpha) - - return grad, hess - - -def _get_quantile_kernel(): - """Lazily compile Quantile gradient kernel.""" - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n, alpha): - idx = cuda.grid(1) - if idx < n: - diff = pred[idx] - y[idx] - if diff > 0: - grad[idx] = 1.0 - alpha # Over-prediction - else: - grad[idx] = -alpha # Under-prediction - hess[idx] = 1.0 - - return kernel - - -_quantile_gradient_kernel = None - -if is_cuda(): - try: - _quantile_gradient_kernel = _get_quantile_kernel() - except Exception: - warnings.warn("Failed to compile Quantile CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Poisson Loss (Count Data) - Phase 9.3 -# ============================================================================= - -def poisson_gradient(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """Compute Poisson deviance gradient and hessian. - - For count data (clicks, purchases, etc.). Predictions are in log-space. - - Loss: L = exp(pred) - y * pred (negative log-likelihood) - Gradient: dL/dpred = exp(pred) - y - Hessian: d²L/dpred² = exp(pred) - - Note: y must be non-negative integers (counts). - """ - if is_cuda(): - return _poisson_gradient_gpu(pred, y) - return _poisson_gradient_cpu(pred, y) - - -def _poisson_gradient_cpu(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """CPU implementation of Poisson gradient.""" - exp_pred = np.exp(np.clip(pred, -20, 20)) # Clip for numerical stability - grad = (exp_pred - y).astype(np.float32) - hess = np.maximum(exp_pred, 1e-6).astype(np.float32) # Hessian = exp(pred) - return grad, hess - - -def _ensure_poisson_kernel(): - global _poisson_gradient_kernel - if _poisson_gradient_kernel is None: - _poisson_gradient_kernel = _get_poisson_kernel() - return _poisson_gradient_kernel - - -def _poisson_gradient_gpu(pred, y): - """GPU implementation of Poisson gradient.""" - from numba import cuda - _ensure_poisson_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _poisson_gradient_kernel[blocks, threads](pred, y, grad, hess, n) - - return grad, hess - - -def _get_poisson_kernel(): - """Lazily compile Poisson gradient kernel.""" - import math - - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n): - idx = cuda.grid(1) - if idx < n: - # Clip for stability - p = pred[idx] - if p > 20: - p = 20.0 - elif p < -20: - p = -20.0 - exp_p = math.exp(p) - grad[idx] = exp_p - y[idx] - hess[idx] = max(exp_p, 1e-6) - - return kernel - - -_poisson_gradient_kernel = None - -if is_cuda(): - try: - _poisson_gradient_kernel = _get_poisson_kernel() - except Exception: - warnings.warn("Failed to compile Poisson CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Gamma Loss (Positive Continuous) - Phase 9.3 -# ============================================================================= - -def gamma_gradient(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """Compute Gamma deviance gradient and hessian. - - For positive continuous data (insurance claims, etc.). Predictions are in log-space. - - Loss: L = y * exp(-pred) + pred (negative log-likelihood, ignoring constants) - Gradient: dL/dpred = 1 - y * exp(-pred) - Hessian: d²L/dpred² = y * exp(-pred) - - Note: y must be strictly positive. - """ - if is_cuda(): - return _gamma_gradient_gpu(pred, y) - return _gamma_gradient_cpu(pred, y) - - -def _gamma_gradient_cpu(pred: NDArray, y: NDArray) -> tuple[NDArray, NDArray]: - """CPU implementation of Gamma gradient.""" - exp_neg_pred = np.exp(np.clip(-pred, -20, 20)) - grad = (1.0 - y * exp_neg_pred).astype(np.float32) - hess = np.maximum(y * exp_neg_pred, 1e-6).astype(np.float32) - return grad, hess - - -def _ensure_gamma_kernel(): - global _gamma_gradient_kernel - if _gamma_gradient_kernel is None: - _gamma_gradient_kernel = _get_gamma_kernel() - return _gamma_gradient_kernel - - -def _gamma_gradient_gpu(pred, y): - """GPU implementation of Gamma gradient.""" - from numba import cuda - _ensure_gamma_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _gamma_gradient_kernel[blocks, threads](pred, y, grad, hess, n) - - return grad, hess - - -def _get_gamma_kernel(): - """Lazily compile Gamma gradient kernel.""" - import math - - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n): - idx = cuda.grid(1) - if idx < n: - neg_p = -pred[idx] - if neg_p > 20: - neg_p = 20.0 - elif neg_p < -20: - neg_p = -20.0 - exp_neg_p = math.exp(neg_p) - y_exp = y[idx] * exp_neg_p - grad[idx] = 1.0 - y_exp - hess[idx] = max(y_exp, 1e-6) - - return kernel - - -_gamma_gradient_kernel = None - -if is_cuda(): - try: - _gamma_gradient_kernel = _get_gamma_kernel() - except Exception: - warnings.warn("Failed to compile Gamma CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Tweedie Loss (Compound Poisson-Gamma) - Phase 9.3 -# ============================================================================= - -def tweedie_gradient(pred: NDArray, y: NDArray, rho: float = 1.5) -> tuple[NDArray, NDArray]: - """Compute Tweedie deviance gradient and hessian. - - Tweedie distribution interpolates between Poisson (rho=1) and Gamma (rho=2). - Commonly used for insurance claims with many zeros. - - For rho in (1, 2), predictions are in log-space: - Loss: L = -y * exp(pred * (1-rho)) / (1-rho) + exp(pred * (2-rho)) / (2-rho) - - Args: - pred: Predictions (in log-space) - y: Targets (non-negative, can have zeros) - rho: Variance power (1 < rho < 2 for compound Poisson-Gamma) - - Note: rho=1.5 is a common default for insurance data. - """ - if is_cuda(): - return _tweedie_gradient_gpu(pred, y, rho) - return _tweedie_gradient_cpu(pred, y, rho) - - -def _tweedie_gradient_cpu(pred: NDArray, y: NDArray, rho: float = 1.5) -> tuple[NDArray, NDArray]: - """CPU implementation of Tweedie gradient.""" - # Clip predictions for numerical stability - pred_clipped = np.clip(pred, -20, 20) - - # mu = exp(pred) - mu = np.exp(pred_clipped) - - # Gradient: mu^(1-rho) * (mu - y) = exp(pred*(2-rho)) - y*exp(pred*(1-rho)) - grad = (np.power(mu, 2 - rho) - y * np.power(mu, 1 - rho)).astype(np.float32) - - # Hessian: (2-rho) * mu^(2-rho) - hess = np.maximum((2 - rho) * np.power(mu, 2 - rho), 1e-6).astype(np.float32) - - return grad, hess - - -def _ensure_tweedie_kernel(): - global _tweedie_gradient_kernel - if _tweedie_gradient_kernel is None: - _tweedie_gradient_kernel = _get_tweedie_kernel() - return _tweedie_gradient_kernel - - -def _tweedie_gradient_gpu(pred, y, rho: float = 1.5): - """GPU implementation of Tweedie gradient.""" - from numba import cuda - _ensure_tweedie_kernel() - - if hasattr(pred, 'copy_to_host'): - n = pred.shape[0] - else: - n = len(pred) - pred = cuda.to_device(np.asarray(pred, dtype=np.float32)) - - if not hasattr(y, 'copy_to_host'): - y = cuda.to_device(np.asarray(y, dtype=np.float32)) - - grad = cuda.device_array(n, dtype=np.float32) - hess = cuda.device_array(n, dtype=np.float32) - - threads = 256 - blocks = (n + threads - 1) // threads - _tweedie_gradient_kernel[blocks, threads](pred, y, grad, hess, n, rho) - - return grad, hess - - -def _get_tweedie_kernel(): - """Lazily compile Tweedie gradient kernel.""" - import math - - from numba import cuda - - @cuda.jit - def kernel(pred, y, grad, hess, n, rho): - idx = cuda.grid(1) - if idx < n: - p = pred[idx] - if p > 20: - p = 20.0 - elif p < -20: - p = -20.0 - - # mu^(2-rho) and mu^(1-rho) via exp - mu_2_rho = math.exp(p * (2.0 - rho)) - mu_1_rho = math.exp(p * (1.0 - rho)) - - grad[idx] = mu_2_rho - y[idx] * mu_1_rho - hess[idx] = max((2.0 - rho) * mu_2_rho, 1e-6) - - return kernel - - -_tweedie_gradient_kernel = None - -if is_cuda(): - try: - _tweedie_gradient_kernel = _get_tweedie_kernel() - except Exception: - warnings.warn("Failed to compile Tweedie CUDA kernel; will retry on first use", stacklevel=1) - - -# ============================================================================= -# Softmax Loss (Multi-class Classification) - Phase 9.2 -# ============================================================================= - -def softmax_gradient(pred: NDArray, y: NDArray, n_classes: int) -> tuple[NDArray, NDArray]: - """Compute Softmax cross-entropy gradient and hessian for multi-class. - - This returns gradients for ALL classes at once. For GBDT, you typically - train K trees per round (one per class). - - Args: - pred: Predictions, shape (n_samples, n_classes) - raw logits - y: Labels, shape (n_samples,) - integer class labels (0 to n_classes-1) - n_classes: Number of classes - - Returns: - grad: Gradients, shape (n_samples, n_classes) - hess: Hessians, shape (n_samples, n_classes) - - Note: For binary classification, use logloss instead (more efficient). - """ - if is_cuda(): - return _softmax_gradient_gpu(pred, y, n_classes) - return _softmax_gradient_cpu(pred, y, n_classes) - - -def _softmax_gradient_cpu(pred: NDArray, y: NDArray, n_classes: int) -> tuple[NDArray, NDArray]: - """CPU implementation of Softmax gradient.""" - n_samples = pred.shape[0] - - # Compute softmax probabilities (with numerical stability) - pred_max = np.max(pred, axis=1, keepdims=True) - exp_pred = np.exp(pred - pred_max) - probs = exp_pred / (np.sum(exp_pred, axis=1, keepdims=True) + 1e-10) - - # One-hot encode y - y_onehot = np.zeros((n_samples, n_classes), dtype=np.float32) - y_onehot[np.arange(n_samples), y.astype(np.int32)] = 1.0 - - # Gradient: prob - y_onehot - grad = (probs - y_onehot).astype(np.float32) - - # Hessian: prob * (1 - prob) for diagonal approximation - hess = (probs * (1 - probs)).astype(np.float32) - hess = np.maximum(hess, 1e-6) # Stability - - return grad, hess - - -def _softmax_gradient_gpu(pred, y, n_classes: int): - """GPU implementation of Softmax gradient.""" - # For simplicity, use CPU implementation and transfer - # TODO: Implement proper CUDA kernel for large-scale - if hasattr(pred, 'copy_to_host'): - pred_cpu = pred.copy_to_host() - else: - pred_cpu = np.asarray(pred, dtype=np.float32) - - y_cpu = y.copy_to_host() if hasattr(y, 'copy_to_host') else np.asarray(y) - - return _softmax_gradient_cpu(pred_cpu, y_cpu, n_classes) - - -# ============================================================================= -# Built-in Loss Registry Seed -# ============================================================================= -# Parameterized built-ins are stored as factories (marked with -# ``__openboost_factory__``) so that get_loss_function can pass kwargs -# (quantile_alpha, tweedie_rho, huber_delta) through to them. - -def _quantile_factory(**kwargs): - alpha = kwargs.get('quantile_alpha', 0.5) - return lambda pred, y: quantile_gradient(pred, y, alpha=alpha) - - -_quantile_factory.__openboost_factory__ = True - - -def _tweedie_factory(**kwargs): - rho = kwargs.get('tweedie_rho', 1.5) - return lambda pred, y: tweedie_gradient(pred, y, rho=rho) - - -_tweedie_factory.__openboost_factory__ = True - - -def _huber_factory(**kwargs): - delta = kwargs.get('huber_delta', kwargs.get('delta', 1.0)) - return lambda pred, y: huber_gradient(pred, y, delta=delta) - - -_huber_factory.__openboost_factory__ = True - - -_LOSS_REGISTRY.update({ - 'mse': mse_gradient, - 'squared_error': mse_gradient, - 'logloss': logloss_gradient, - 'binary_crossentropy': logloss_gradient, - 'huber': _huber_factory, - 'mae': mae_gradient, - 'l1': mae_gradient, - 'absolute_error': mae_gradient, - 'poisson': poisson_gradient, - 'gamma': gamma_gradient, - 'quantile': _quantile_factory, - 'tweedie': _tweedie_factory, -}) -_BUILTIN_LOSS_SEED.update(_LOSS_REGISTRY) - diff --git a/src/openboost/_models/__init__.py b/src/openboost/_models/__init__.py deleted file mode 100644 index 03e3b49..0000000 --- a/src/openboost/_models/__init__.py +++ /dev/null @@ -1,89 +0,0 @@ -"""High-level models built on the core infrastructure. - -These models provide scikit-learn-like APIs and use fit_tree() -from the core module to build trees. - -Phase 13: Added sklearn-compatible wrappers. -Phase 15: Added distributional GBDT, NaturalBoost, and linear leaf GBDT. -Phase 16: Renamed NGBoost -> NaturalBoost for clarity. -""" - -from ._boosting import GradientBoosting, MultiClassGradientBoosting -from ._dart import DART - -# Phase 15/16: Distributional GBDT and NaturalBoost -from ._distributional import ( - DistributionalGBDT, - # Primary names (Phase 16) - NaturalBoost, - NaturalBoostGamma, - NaturalBoostLogNormal, - NaturalBoostNegBin, - NaturalBoostNormal, - NaturalBoostPoisson, - NaturalBoostStudentT, - NaturalBoostTweedie, - # Backward compatibility aliases - NGBoost, - NGBoostGamma, - NGBoostLogNormal, - NGBoostNegBin, - NGBoostNormal, - NGBoostPoisson, - NGBoostStudentT, - NGBoostTweedie, -) -from ._formula import FormulaBoost -from ._gam import OpenBoostGAM - -# Phase 15: Linear Leaf GBDT -from ._linear_leaf import LinearLeafGBDT, LinearLeafTree -from ._sklearn import ( - OpenBoostClassifier, - OpenBoostDARTRegressor, - OpenBoostDistributionalRegressor, - OpenBoostGAMRegressor, - OpenBoostLinearLeafRegressor, - OpenBoostRegressor, -) -from ._survival import WeibullAFT - -__all__ = [ - # Standard GBDT - "GradientBoosting", - "MultiClassGradientBoosting", - "DART", - "OpenBoostGAM", - # Phase 13: sklearn-compatible wrappers - "OpenBoostRegressor", - "OpenBoostClassifier", - # Phase 15+: sklearn wrappers for new models - "OpenBoostDARTRegressor", - "OpenBoostGAMRegressor", - "OpenBoostDistributionalRegressor", - "OpenBoostLinearLeafRegressor", - # Phase 15/16: Distributional GBDT - Primary names - "DistributionalGBDT", - "NaturalBoost", - "NaturalBoostNormal", - "NaturalBoostLogNormal", - "NaturalBoostGamma", - "NaturalBoostPoisson", - "NaturalBoostStudentT", - "NaturalBoostTweedie", - "NaturalBoostNegBin", - # Backward compatibility (deprecated) - "NGBoost", - "NGBoostNormal", - "NGBoostLogNormal", - "NGBoostGamma", - "NGBoostPoisson", - "NGBoostStudentT", - "NGBoostTweedie", - "NGBoostNegBin", - # Phase 15: Linear Leaf GBDT - "LinearLeafGBDT", - "LinearLeafTree", - "FormulaBoost", - "WeibullAFT", -] diff --git a/src/openboost/_models/_boosting.py b/src/openboost/_models/_boosting.py deleted file mode 100644 index 12ab4cc..0000000 --- a/src/openboost/_models/_boosting.py +++ /dev/null @@ -1,1537 +0,0 @@ -"""Gradient Boosting ensemble model for OpenBoost. - -Provides a scikit-learn-like API for training gradient boosting models -with both built-in and custom loss functions. - -This module implements batched training that keeps computation on the GPU -without returning to Python between trees, achieving performance competitive -with XGBoost. - -Phase 13: Added callback support for early stopping, logging, etc. -Phase 17: Added GOSS sampling and mini-batch training for large-scale datasets. -Phase 18: Added multi-GPU support via Ray for data-parallel training. -""" - -from __future__ import annotations - -import os -import warnings -from collections.abc import Callable -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Literal - -import numpy as np - -from .._array import BinnedArray, array -from .._backends import is_cuda -from .._callbacks import ( - Callback, - CallbackManager, - TrainingState, - warn_if_early_stopping_without_eval_set, -) -from .._core._growth import TreeStructure, get_growth_strategy -from .._core._tree import fit_tree -from .._loss import LossFunction, compute_loss_value, get_loss_function, is_builtin_loss -from .._persistence import PersistenceMixin -from .._sampling import ( - goss_sample, -) -from .._validation import ( - validate_eval_set, - validate_hyperparameters, - validate_predict_input, - validate_sample_weight, - validate_X, - validate_y, -) - -try: - from .._distributed._ray import RayDistributedContext - from .._distributed._tree import fit_tree_distributed -except ImportError: - RayDistributedContext = None - fit_tree_distributed = None - -try: - from .._distributed._multigpu import MultiGPUContext, fit_tree_multigpu -except ImportError: - MultiGPUContext = None - fit_tree_multigpu = None - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -def _is_levelwise_growth(growth) -> bool: - """True if *growth* names the level-wise strategy (any accepted alias).""" - return isinstance(growth, str) and growth.lower() in ( - 'levelwise', 'level_wise', 'level-wise' - ) - - -def _compute_loss_value(loss, pred, y, **kwargs) -> float: - """Compute scalar loss using the true loss formula for known objectives. - - Delegates to ``compute_loss_value`` in ``_loss.py`` which computes the - actual loss (MSE, MAE, logloss, …) rather than the second-order Taylor - proxy that is unreliable for MAE/quantile. - - *loss* may be a string name or a callable. For callables, a - ``loss_value`` attribute on the callable (``fn.loss_value = ...``) is - used as the true loss when present; otherwise the grad/hess proxy is - the fallback. - """ - if hasattr(pred, 'copy_to_host'): - pred = pred.copy_to_host() - pred = np.asarray(pred, dtype=np.float64) - y = np.asarray(y, dtype=np.float64) - - if isinstance(loss, str): - return compute_loss_value(loss, pred, y, **kwargs) - - # Custom callable with an explicit true-loss hook - value_fn = getattr(loss, 'loss_value', None) - if value_fn is not None: - return float(value_fn(pred, y)) - - # Custom callable — use grad/hess proxy - grad, hess = loss(np.asarray(pred, dtype=np.float32), - np.asarray(y, dtype=np.float32)) - if hasattr(grad, 'copy_to_host'): - grad = grad.copy_to_host() - if hasattr(hess, 'copy_to_host'): - hess = hess.copy_to_host() - grad = np.asarray(grad, dtype=np.float64) - hess = np.maximum(np.asarray(hess, dtype=np.float64), 1e-10) - return float(np.mean(grad ** 2 / (2.0 * hess))) - - -@dataclass -class GradientBoosting(PersistenceMixin): - """Gradient Boosting ensemble model. - - A gradient boosting model that supports both built-in loss functions - and custom loss functions. When using built-in losses with GPU, - training is fully batched for maximum performance. - - Args: - n_trees: Number of trees to train. - max_depth: Maximum depth of each tree. - learning_rate: Shrinkage factor applied to each tree. - loss: Loss function. Can be: - - 'mse': Mean Squared Error (regression) - - 'logloss': Binary cross-entropy (classification) - - 'huber': Huber loss (robust regression) - - 'mae': Mean Absolute Error (L1 regression) - - 'quantile': Quantile regression (use with quantile_alpha) - - Callable: Custom function(pred, y) -> (grad, hess) - min_child_weight: Minimum sum of hessian in a leaf. - reg_lambda: L2 regularization on leaf values. - n_bins: Number of bins for histogram building. - quantile_alpha: Quantile level for 'quantile' loss (0 < alpha < 1). - - 0.5: Median regression (default) - - 0.9: 90th percentile - - 0.1: 10th percentile - tweedie_rho: Variance power for 'tweedie' loss (1 < rho < 2). - - 1.5: Default (compound Poisson-Gamma) - subsample_strategy: Sampling strategy for large-scale training (Phase 17): - - 'none': No sampling (default) - - 'random': Random subsampling - - 'goss': Gradient-based One-Side Sampling (LightGBM-style) - goss_top_rate: Fraction of top-gradient samples to keep (for GOSS). - goss_other_rate: Fraction of remaining samples to sample (for GOSS). - batch_size: Mini-batch size for large datasets. If None, process all at once. - growth: Tree growth strategy: - - 'levelwise': XGBoost-style level-wise growth (default) - - 'leafwise': LightGBM-style best-first growth (see max_leaves) - - 'symmetric': CatBoost-style oblivious trees - max_leaves: Maximum number of leaves per tree (used by 'leafwise' - growth; defaults to 2**max_depth when None). - - Examples: - Basic regression: - - ```python - import openboost as ob - - model = ob.GradientBoosting(n_trees=100, loss='mse') - model.fit(X_train, y_train) - predictions = model.predict(X_test) - ``` - - Quantile regression (90th percentile): - - ```python - model = ob.GradientBoosting(loss='quantile', quantile_alpha=0.9) - model.fit(X_train, y_train) - ``` - - GOSS for faster training: - - ```python - model = ob.GradientBoosting( - n_trees=100, - subsample_strategy='goss', - goss_top_rate=0.2, - goss_other_rate=0.1, - ) - ``` - - Multi-GPU training: - - ```python - model = ob.GradientBoosting(n_trees=100, n_gpus=4) - model.fit(X, y) # Data parallel across 4 GPUs - ``` - """ - - n_trees: int = 100 - max_depth: int = 6 - learning_rate: float = 0.1 - loss: str | LossFunction | Callable[..., tuple] = 'mse' - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 - gamma: float = 0.0 - subsample: float = 1.0 - colsample_bytree: float = 1.0 - n_bins: int = 254 - quantile_alpha: float = 0.5 - tweedie_rho: float = 1.5 - distributed: bool = False - n_workers: int | None = None - subsample_strategy: Literal['none', 'random', 'goss'] = 'none' - goss_top_rate: float = 0.2 - goss_other_rate: float = 0.1 - batch_size: int | None = None - n_gpus: int | None = None - devices: list[int] | None = None - random_state: int | None = None - growth: str = 'levelwise' - max_leaves: int | None = None - - # Fitted attributes (not init) - trees_: list[TreeStructure] = field(default_factory=list, init=False, repr=False) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _loss_fn: LossFunction | None = field(default=None, init=False, repr=False) - n_features_in_: int = field(default=0, init=False, repr=False) # Phase 20.3: store for validation - - def fit( - self, - X: NDArray, - y: NDArray, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - sample_weight: NDArray | None = None, - ) -> GradientBoosting: - """Fit the gradient boosting model. - - Args: - X: Training features, shape (n_samples, n_features). - y: Training targets, shape (n_samples,). - callbacks: List of Callback instances for training hooks. - Use EarlyStopping for early stopping, Logger for progress. - eval_set: List of (X, y) tuples for validation (used with callbacks). - sample_weight: Sample weights, shape (n_samples,). - - Returns: - self: The fitted model. - - Example: - ```python - from openboost import GradientBoosting, EarlyStopping, Logger - - model = GradientBoosting(n_trees=1000) - model.fit( - X, y, - callbacks=[EarlyStopping(patience=50), Logger(period=10)], - eval_set=[(X_val, y_val)] - ) - ``` - """ - # Clear any previous fit - self.trees_ = [] - - # Auto-enable profiling via env var - if os.environ.get("OPENBOOST_PROFILE"): - from .._profiler import ProfilingCallback - _profile_dir = os.environ.get("OPENBOOST_PROFILE_DIR", "logs/") - callbacks = list(callbacks or []) + [ProfilingCallback(output_dir=_profile_dir)] - - # Validate inputs (Phase 20.3) - X = validate_X(X, allow_nan=True, context="fit") - y = validate_y(y, n_samples=X.shape[0] if hasattr(X, 'shape') else None, context="fit") - sample_weight = validate_sample_weight(sample_weight, len(y)) - - use_multigpu = ( - (self.n_gpus is not None and self.n_gpus > 1) - or (self.devices is not None and len(self.devices) > 1) - ) - if sample_weight is not None: - if use_multigpu: - raise NotImplementedError( - "sample_weight is not supported for multi-GPU training; " - "use a single CPU backend instead" - ) - if self.distributed: - raise NotImplementedError( - "sample_weight is not supported for distributed training; " - "use the CPU backend instead" - ) - if is_cuda(): - raise NotImplementedError( - "sample_weight is not supported on the CUDA backend; " - "use ob.set_backend('cpu') before fitting" - ) - - n_samples = len(y) - - # Validate hyperparameters - validate_hyperparameters( - n_trees=self.n_trees, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - subsample=self.subsample, - n_samples=n_samples, - ) - - # Validate growth strategy early (clear error before any training work) - if isinstance(self.growth, str): - get_growth_strategy(self.growth) - - # Initialize RNG for reproducibility - self._rng = np.random.default_rng(self.random_state) - - # Get loss function (pass parameters for parameterized losses) - self._loss_fn = get_loss_function( - self.loss, - quantile_alpha=self.quantile_alpha, - tweedie_rho=self.tweedie_rho, - ) - - # Validate eval_set - if eval_set is not None: - n_features = X.shape[1] if hasattr(X, 'shape') else X.n_features - eval_set = validate_eval_set(eval_set, n_features) - - # Bin the data (this is the expensive step, but only done once) - if isinstance(X, BinnedArray): - self.X_binned_ = X - else: - self.X_binned_ = array(X, n_bins=self.n_bins) - - # Store for feature importance - self.n_features_in_ = self.X_binned_.n_features - - # LOW-12: Initialize base score - loss_name = self.loss if isinstance(self.loss, str) else '' - if loss_name in ('logloss', 'binary_crossentropy'): - p = np.clip(np.mean(y), 1e-7, 1 - 1e-7) - self.base_score_ = np.float32(np.log(p / (1 - p))) - else: - self.base_score_ = np.float32(np.mean(y)) - - # Choose training path based on backend - # Phase 18: Check for multi-GPU first - if (use_multigpu or self.distributed) and not _is_levelwise_growth(self.growth): - warnings.warn( - f"growth='{self.growth}' is not supported for distributed/" - "multi-GPU training; using levelwise growth instead.", - UserWarning, - stacklevel=2, - ) - - if use_multigpu: - self._fit_multigpu(X, y, n_samples, callbacks, eval_set) - elif self.distributed: - self._fit_distributed(y, n_samples) - elif is_cuda(): - self._fit_gpu(y, n_samples, callbacks, eval_set) - else: - self._fit_cpu(y, n_samples, callbacks, eval_set, sample_weight) - - return self - - def _fit_distributed(self, y: NDArray, n_samples: int): - """Distributed training using Ray.""" - if RayDistributedContext is None: - raise ImportError("Distributed training requires 'ray'. Install with 'pip install ray'.") - - ctx = RayDistributedContext(self.n_workers) - - X_data = self.X_binned_.data - if hasattr(X_data, 'copy_to_host'): - X_data = X_data.copy_to_host() - - ctx.setup(X_data, y, self.n_bins) - - - for _i in range(self.n_trees): - # Compute gradients on each worker - grad_hess_refs = [ - w.compute_gradients.options(num_returns=2).remote(self._loss_fn) - for w in ctx.workers - ] - - grad_refs = [pair[0] for pair in grad_hess_refs] - hess_refs = [pair[1] for pair in grad_hess_refs] - - # Distributed tree fitting - tree = fit_tree_distributed( - ctx, - ctx.workers, - grad_refs, - hess_refs, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - min_gain=self.gamma, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - ) - - # Update predictions on each worker - for w in ctx.workers: - w.update_predictions.remote(tree, self.learning_rate) - - self.trees_.append(tree) - - def _fit_multigpu( - self, - X: NDArray, - y: NDArray, - n_samples: int, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ): - """Multi-GPU training using Ray actors (Phase 18). - - Each GPU holds a shard of the data and computes local histograms, - which are aggregated on the driver to build global trees. - - This approach provides near-linear scaling for large datasets. - """ - if MultiGPUContext is None: - raise ImportError( - "Multi-GPU training requires Ray. " - "Install with: pip install 'openboost[distributed]'" - ) - - # Setup callbacks - cb_manager = CallbackManager(callbacks) - state = TrainingState(model=self, n_rounds=self.n_trees) - cb_manager.on_train_begin(state) - - # Create multi-GPU context and setup workers - ctx = MultiGPUContext(n_gpus=self.n_gpus, devices=self.devices) - - # Get raw data for sharding - X_data = X if not isinstance(X, BinnedArray) else None - if X_data is None: - # Need to get unbinned data for multi-GPU - # For now, require raw data input - raise ValueError( - "Multi-GPU training requires raw (unbinned) data input. " - "Pass X as a numpy array, not BinnedArray." - ) - - ctx.setup(X_data, y, n_bins=self.n_bins) - - try: - - # Training loop - for i in range(self.n_trees): - state.round_idx = i - cb_manager.on_round_begin(state) - - # 1. Compute gradients on each GPU (parallel) - grads_hess = ctx.compute_all_gradients(self._loss_fn) - - # 2. Build local histograms on each GPU (parallel) - local_histograms = ctx.build_all_histograms(grads_hess) - - # 3. Aggregate histograms on driver - global_hist_grad, global_hist_hess = ctx.aggregate_histograms(local_histograms) - - # 4. Build tree from global histogram - # Concatenate gradients for full tree fitting - all_grad = np.concatenate([g for g, h in grads_hess]) - all_hess = np.concatenate([h for g, h in grads_hess]) - - # For proper tree building, we need the full binned data - # Use a simplified approach: fit tree on driver with full histogram info - - tree = self._build_tree_from_histogram( - global_hist_grad, - global_hist_hess, - all_grad, - all_hess, - ctx.n_features, - ) - - self.trees_.append(tree) - - # 5. Update predictions on each GPU (parallel) - ctx.update_all_predictions(tree, self.learning_rate) - - # Compute losses for callbacks (requires collecting predictions) - if callbacks or eval_set: - all_pred = ctx.get_all_predictions() - state.train_loss = float(np.mean((all_pred - y) ** 2)) - - if eval_set: - X_val, y_val = eval_set[0] - val_pred = self.predict(X_val) - state.val_loss = float(np.mean((val_pred - y_val) ** 2)) - - # Check if callbacks want to stop - if not cb_manager.on_round_end(state): - break - - cb_manager.on_train_end(state) - - finally: - # Cleanup - ctx.shutdown() - - def _build_tree_from_histogram( - self, - hist_grad: NDArray, - hist_hess: NDArray, - all_grad: NDArray, - all_hess: NDArray, - n_features: int, - ) -> TreeStructure: - """Build a tree from aggregated histogram for multi-GPU training. - - Uses recursive histogram-based tree building similar to LightGBM. - """ - from .._core._growth import TreeStructure - from .._core._split import compute_leaf_value, find_best_split - - max_nodes = 2**(self.max_depth + 1) - 1 - features = np.full(max_nodes, -1, dtype=np.int32) - thresholds = np.zeros(max_nodes, dtype=np.int32) - values = np.zeros(max_nodes, dtype=np.float32) - left_children = np.full(max_nodes, -1, dtype=np.int32) - right_children = np.full(max_nodes, -1, dtype=np.int32) - - # Build tree level by level using histogram - # Start with root node (all samples) - node_hist_grad = {0: hist_grad.copy()} - node_hist_hess = {0: hist_hess.copy()} - node_sum_grad = {0: float(np.sum(all_grad))} - node_sum_hess = {0: float(np.sum(all_hess))} - - active_nodes = [0] - next_node_id = 1 - actual_depth = 0 - - for depth in range(self.max_depth): - if not active_nodes: - break - - actual_depth = depth + 1 - new_active_nodes = [] - - for node_id in active_nodes: - h_grad = node_hist_grad.get(node_id) - h_hess = node_hist_hess.get(node_id) - s_grad = node_sum_grad.get(node_id, 0.0) - s_hess = node_sum_hess.get(node_id, 0.0) - - if h_grad is None or s_hess < self.min_child_weight: - # Make leaf - values[node_id] = compute_leaf_value(s_grad, s_hess, self.reg_lambda, self.reg_alpha) - continue - - # Find best split - split = find_best_split( - h_grad, h_hess, - s_grad, s_hess, - reg_lambda=self.reg_lambda, - min_child_weight=self.min_child_weight, - min_gain=self.gamma, - ) - - if not split.is_valid: - # Make leaf - values[node_id] = compute_leaf_value(s_grad, s_hess, self.reg_lambda, self.reg_alpha) - continue - - # Apply split - features[node_id] = split.feature - thresholds[node_id] = split.threshold - left_children[node_id] = next_node_id - right_children[node_id] = next_node_id + 1 - - # Compute left/right histogram sums - left_grad = float(np.sum(h_grad[split.feature, :split.threshold + 1])) - left_hess = float(np.sum(h_hess[split.feature, :split.threshold + 1])) - right_grad = s_grad - left_grad - right_hess = s_hess - left_hess - - # Create child histograms. - # For the split feature we can partition exactly by threshold. - # For non-split features the true per-bin distribution depends - # on sample assignments which we don't have here, so we - # approximate by scaling the parent histogram proportionally - # (by the fraction of hessian going to each child). - sf = split.feature - frac_left = left_hess / s_hess if s_hess > 0 else 0.5 - frac_right = 1.0 - frac_left - - left_hist_grad = h_grad * frac_left - left_hist_hess = h_hess * frac_left - # Split feature: exact partition replaces the approximation - left_hist_grad[sf, :] = 0.0 - left_hist_grad[sf, :split.threshold + 1] = h_grad[sf, :split.threshold + 1] - left_hist_hess[sf, :] = 0.0 - left_hist_hess[sf, :split.threshold + 1] = h_hess[sf, :split.threshold + 1] - - right_hist_grad = h_grad * frac_right - right_hist_hess = h_hess * frac_right - right_hist_grad[sf, :] = 0.0 - right_hist_grad[sf, split.threshold + 1:] = h_grad[sf, split.threshold + 1:] - right_hist_hess[sf, :] = 0.0 - right_hist_hess[sf, split.threshold + 1:] = h_hess[sf, split.threshold + 1:] - - # Store child info - left_id = next_node_id - right_id = next_node_id + 1 - - node_hist_grad[left_id] = left_hist_grad - node_hist_hess[left_id] = left_hist_hess - node_sum_grad[left_id] = left_grad - node_sum_hess[left_id] = left_hess - - node_hist_grad[right_id] = right_hist_grad - node_hist_hess[right_id] = right_hist_hess - node_sum_grad[right_id] = right_grad - node_sum_hess[right_id] = right_hess - - new_active_nodes.extend([left_id, right_id]) - next_node_id += 2 - - active_nodes = new_active_nodes - - # Compute leaf values for remaining active nodes - for node_id in active_nodes: - s_grad = node_sum_grad.get(node_id, 0.0) - s_hess = node_sum_hess.get(node_id, 0.0) - values[node_id] = compute_leaf_value(s_grad, s_hess, self.reg_lambda, self.reg_alpha) - - # Trim arrays - n_nodes = next_node_id - - return TreeStructure( - features=features[:n_nodes], - thresholds=thresholds[:n_nodes], - values=values[:n_nodes], - left_children=left_children[:n_nodes], - right_children=right_children[:n_nodes], - n_nodes=n_nodes, - depth=actual_depth, - n_features=n_features, - ) - - def _fit_gpu( - self, - y: NDArray, - n_samples: int, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ): - """GPU-optimized training using growth strategies with callback support. - - Phase 17: Added GOSS sampling for faster training on large datasets. - """ - from numba import cuda - - # Setup callbacks - cb_manager = CallbackManager(callbacks) - state = TrainingState(model=self, n_rounds=self.n_trees) - cb_manager.on_train_begin(state) - - warn_if_early_stopping_without_eval_set(callbacks, eval_set) - - # Move y to GPU - y_gpu = cuda.to_device(y) - - # Initialize predictions on GPU with base score - base = getattr(self, 'base_score_', np.float32(0.0)) - pred_host = np.full(n_samples, base, dtype=np.float32) - pred_gpu = cuda.to_device(pred_host) - - # Check if using custom loss (requires a Python callback unless the - # loss is marked device-native). Losses registered by name via - # register_loss() count as custom too. - is_custom_loss = callable(self.loss) or not is_builtin_loss(self.loss) - # Opt-in device contract: a callable decorated with - # @openboost.device_loss (i.e. ``__openboost_device__ = True``) - # receives device arrays directly and must return device (grad, hess). - is_device_loss = is_custom_loss and bool( - getattr(self._loss_fn, '__openboost_device__', False) - ) - - # Pre-allocate gradient arrays - if not is_custom_loss: - grad_gpu = cuda.device_array(n_samples, dtype=np.float32) - hess_gpu = cuda.device_array(n_samples, dtype=np.float32) - - # Fast MSE path: hessian is constant 1.0 — fill once, reuse every - # iteration. Use in-place gradient kernel to avoid cudaMalloc each round. - _use_fast_mse = not is_custom_loss and self.loss == 'mse' - if _use_fast_mse: - cuda.to_device( - np.full(n_samples, 1.0, dtype=np.float32), to=hess_gpu - ) - from .._backends._cuda import mse_grad_inplace_gpu - - # Fast logloss path: use in-place kernel to avoid cudaMalloc each round. - _use_fast_logloss = not is_custom_loss and self.loss in ('logloss', 'binary_crossentropy') - if _use_fast_logloss: - from .._backends._cuda import logloss_grad_inplace_gpu - - # Determine sampling strategy - use_goss = self.subsample_strategy == 'goss' - use_random_sampling = self.subsample_strategy == 'random' and self.subsample < 1.0 - - # Pre-compute GPU-native eligibility (constant across iterations) - has_missing = ( - hasattr(self.X_binned_, 'has_missing') - and len(self.X_binned_.has_missing) > 0 - and np.any(self.X_binned_.has_missing) - ) - has_categorical = ( - hasattr(self.X_binned_, 'is_categorical') - and len(self.X_binned_.is_categorical) > 0 - and np.any(self.X_binned_.is_categorical) - ) - # The GPU-native builder only implements level-wise growth; other - # strategies fall back to the standard fit_tree() path below. - growth_is_levelwise = _is_levelwise_growth(self.growth) - _use_gpu_native = ( - not use_goss - and not use_random_sampling - and is_cuda() - and self.reg_alpha == 0.0 - and self.colsample_bytree >= 1.0 - and self.subsample >= 1.0 - and not has_missing - and not has_categorical - and growth_is_levelwise - ) - if is_cuda() and not _use_gpu_native: - reasons = [] - if self.reg_alpha != 0.0: - reasons.append("reg_alpha != 0") - if self.colsample_bytree < 1.0: - reasons.append("colsample_bytree < 1.0") - if self.subsample < 1.0: - reasons.append("subsample < 1.0") - if has_missing: - reasons.append("data contains missing values") - if has_categorical: - reasons.append("data contains categorical features") - if not growth_is_levelwise: - reasons.append( - f"growth='{self.growth}' (GPU-native builder only supports levelwise)" - ) - if reasons: - warnings.warn( - f"GPU-native tree builder not available ({', '.join(reasons)}). " - "Falling back to standard fit_tree(). This is slower but " - "functionally identical.", - UserWarning, - stacklevel=2, - ) - if _use_gpu_native: - from .._core._tree import fit_tree_gpu_native - max_nodes = 2**(self.max_depth + 1) - 1 - # Use async D2D copies when no callbacks need self.trees_ - # during training (avoids BOTH cudaMalloc AND copy_to_host sync). - # Any callback (ModelCheckpoint, EarlyStopping, etc.) may inspect - # state.model.trees_, so materialize trees per-round when present. - _use_d2d = not cb_manager.callbacks - if _use_d2d: - _buf_features = cuda.device_array(self.n_trees * max_nodes, dtype=np.int32) - _buf_thresholds = cuda.device_array(self.n_trees * max_nodes, dtype=np.int32) - _buf_values = cuda.device_array(self.n_trees * max_nodes, dtype=np.float32) - _buf_left = cuda.device_array(self.n_trees * max_nodes, dtype=np.int32) - _buf_right = cuda.device_array(self.n_trees * max_nodes, dtype=np.int32) - from .._backends._cuda import _copy_to_slot_kernel - _copy_threads = 256 - _copy_blocks = (max_nodes + _copy_threads - 1) // _copy_threads - _n_trees_built = 0 - # Pre-transfer binned data to GPU once to avoid repeated H2D - # copies (Numba auto-transfers numpy arrays on every kernel call). - _binned_data = self.X_binned_.data - if not hasattr(_binned_data, '__cuda_array_interface__'): - _binned_gpu = cuda.to_device(_binned_data) - else: - _binned_gpu = _binned_data - - # Train trees - for i in range(self.n_trees): - state.round_idx = i - cb_manager.on_round_begin(state) - - # Compute gradients - if is_device_loss: - # Device-native custom loss: pred stays on the GPU, y is the - # cached device copy; the callable must return device - # (grad, hess) arrays (no host round-trip). - grad_gpu, hess_gpu = self._loss_fn(pred_gpu, y_gpu) - elif is_custom_loss: - # Custom loss: need to copy pred to CPU, call Python, copy back - pred_cpu = pred_gpu.copy_to_host() - grad_cpu, hess_cpu = self._loss_fn(pred_cpu, y) - grad_gpu = cuda.to_device(grad_cpu.astype(np.float32)) - hess_gpu = cuda.to_device(hess_cpu.astype(np.float32)) - elif _use_fast_mse: - # Fast MSE: in-place grad, constant hess (zero allocation) - mse_grad_inplace_gpu(pred_gpu, y_gpu, grad_gpu) - elif _use_fast_logloss: - # Fast logloss: in-place grad+hess (zero allocation) - logloss_grad_inplace_gpu(pred_gpu, y_gpu, grad_gpu, hess_gpu) - else: - # Built-in loss: compute entirely on GPU - grad_gpu, hess_gpu = self._loss_fn(pred_gpu, y_gpu) - - # Apply sampling strategy (Phase 17) - if use_goss: - # GOSS: Compute sampling on CPU (requires sorting), then apply weights - grad_cpu = grad_gpu.copy_to_host() if hasattr(grad_gpu, 'copy_to_host') else grad_gpu - hess_cpu = hess_gpu.copy_to_host() if hasattr(hess_gpu, 'copy_to_host') else hess_gpu - - sample_result = goss_sample( - grad_cpu, hess_cpu, - top_rate=self.goss_top_rate, - other_rate=self.goss_other_rate, - seed=int(self._rng.integers(2**31)), - ) - - # Create weighted gradient/hessian arrays - # Zero out non-sampled samples, apply weights to sampled samples - grad_goss = np.zeros_like(grad_cpu) - hess_goss = np.zeros_like(hess_cpu) - grad_goss[sample_result.indices] = grad_cpu[sample_result.indices] * sample_result.weights - hess_goss[sample_result.indices] = hess_cpu[sample_result.indices] * sample_result.weights - - # Transfer to GPU - grad_goss_gpu = cuda.to_device(grad_goss.astype(np.float32)) - hess_goss_gpu = cuda.to_device(hess_goss.astype(np.float32)) - - # Build tree with GOSS-weighted gradients - tree = fit_tree( - self.X_binned_, - grad_goss_gpu, - hess_goss_gpu, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=1.0, - colsample_bytree=self.colsample_bytree, - ) - - state.extra['goss_sample_rate'] = sample_result.sample_rate - elif use_random_sampling: - # Random subsampling (use built-in subsample parameter) - tree = fit_tree( - self.X_binned_, - grad_gpu, - hess_gpu, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - ) - elif _use_gpu_native: - legacy_tree = fit_tree_gpu_native( - _binned_gpu, - grad_gpu, - hess_gpu, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - min_gain=self.gamma, - pred_gpu=pred_gpu, - learning_rate=self.learning_rate, - const_hess=1.0 if _use_fast_mse else 0.0, - ) - if _use_d2d: - # Async D2D copy from workspace → pre-allocated buffer. - # No sync barrier; next tree build starts immediately. - slot = _n_trees_built * max_nodes - f_gpu, t_gpu, v_gpu, l_gpu, r_gpu = legacy_tree.to_gpu_arrays() - _copy_to_slot_kernel[_copy_blocks, _copy_threads](f_gpu, _buf_features, slot, max_nodes) - _copy_to_slot_kernel[_copy_blocks, _copy_threads](t_gpu, _buf_thresholds, slot, max_nodes) - _copy_to_slot_kernel[_copy_blocks, _copy_threads](v_gpu, _buf_values, slot, max_nodes) - _copy_to_slot_kernel[_copy_blocks, _copy_threads](l_gpu, _buf_left, slot, max_nodes) - _copy_to_slot_kernel[_copy_blocks, _copy_threads](r_gpu, _buf_right, slot, max_nodes) - else: - # Fallback: sync copy for callbacks needing self.trees_ - features, thresholds, values, left, right = legacy_tree.to_arrays() - tree = TreeStructure( - features=features, - thresholds=thresholds, - left_children=left, - right_children=right, - values=values, - n_nodes=len(features), - depth=legacy_tree.depth, - n_features=legacy_tree.n_features, - ) - self.trees_.append(tree) - _n_trees_built += 1 - else: - tree = fit_tree( - self.X_binned_, - grad_gpu, - hess_gpu, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - ) - # Update predictions on GPU - tree_pred = tree(self.X_binned_) - if hasattr(tree_pred, '__cuda_array_interface__'): - from .._core._predict import _add_inplace_cuda - _add_inplace_cuda(pred_gpu, tree_pred, self.learning_rate) - else: - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred_cpu = pred_gpu.copy_to_host() - pred_cpu += self.learning_rate * tree_pred - cuda.to_device(pred_cpu, to=pred_gpu) - self.trees_.append(tree) - - # Only compute loss and copy to CPU when callbacks need it - if cb_manager.callbacks: - pred_cpu = pred_gpu.copy_to_host() - state.train_loss = _compute_loss_value(self.loss, pred_cpu, y, quantile_alpha=self.quantile_alpha, tweedie_rho=self.tweedie_rho) - - if eval_set: - X_val, y_val = eval_set[0] - val_pred = self.predict(X_val) - state.val_loss = _compute_loss_value(self.loss, val_pred, y_val, quantile_alpha=self.quantile_alpha, tweedie_rho=self.tweedie_rho) - - # Check if callbacks want to stop - if not cb_manager.on_round_end(state): - break - - # Batch-convert GPU tree buffers to TreeStructure on CPU - if _use_gpu_native and _use_d2d and _n_trees_built > 0: - all_f = _buf_features.copy_to_host() - all_t = _buf_thresholds.copy_to_host() - all_v = _buf_values.copy_to_host() - all_l = _buf_left.copy_to_host() - all_r = _buf_right.copy_to_host() - n_feat = self.X_binned_.n_features - for j in range(_n_trees_built): - s = j * max_nodes - e = s + max_nodes - tree = TreeStructure( - features=all_f[s:e].copy(), - thresholds=all_t[s:e].copy(), - left_children=all_l[s:e].copy(), - right_children=all_r[s:e].copy(), - values=all_v[s:e].copy(), - n_nodes=max_nodes, - depth=self.max_depth, - n_features=n_feat, - ) - self.trees_.append(tree) - - cb_manager.on_train_end(state) - - def _fit_cpu( - self, - y: NDArray, - n_samples: int, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - sample_weight: NDArray | None = None, - ): - """CPU training path with callback support. - - Phase 17: Added GOSS sampling and mini-batch training. - """ - # Setup callbacks - cb_manager = CallbackManager(callbacks) - state = TrainingState(model=self, n_rounds=self.n_trees) - cb_manager.on_train_begin(state) - - warn_if_early_stopping_without_eval_set(callbacks, eval_set) - - # Initialize predictions with base score - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples, base, dtype=np.float32) - - # Determine sampling strategy - use_goss = self.subsample_strategy == 'goss' - use_random_sampling = self.subsample_strategy == 'random' and self.subsample < 1.0 - - # Train trees - for i in range(self.n_trees): - state.round_idx = i - cb_manager.on_round_begin(state) - - # Compute gradients - grad, hess = self._loss_fn(pred, y) - grad = grad.astype(np.float32) - hess = hess.astype(np.float32) - - # Apply sample weights if provided - if sample_weight is not None: - weights = np.asarray(sample_weight, dtype=np.float32) - grad = grad * weights - hess = hess * weights - - # Apply sampling strategy (Phase 17) - if use_goss: - # GOSS: Keep high-gradient samples, subsample rest - sample_result = goss_sample( - grad, hess, - top_rate=self.goss_top_rate, - other_rate=self.goss_other_rate, - seed=int(self._rng.integers(2**31)), - ) - - # Create weighted gradient/hessian arrays - # Zero out non-sampled samples, apply weights to sampled samples - grad_goss = np.zeros_like(grad) - hess_goss = np.zeros_like(hess) - grad_goss[sample_result.indices] = grad[sample_result.indices] * sample_result.weights - hess_goss[sample_result.indices] = hess[sample_result.indices] * sample_result.weights - - # Build tree with GOSS-weighted gradients - tree = fit_tree( - self.X_binned_, - grad_goss, - hess_goss, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=1.0, # Already sampled via GOSS - colsample_bytree=self.colsample_bytree, - ) - elif use_random_sampling: - # Random subsampling (use built-in subsample parameter) - tree = fit_tree( - self.X_binned_, - grad, - hess, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - ) - else: - # Standard training (with optional row/col subsampling in fit_tree) - tree = fit_tree( - self.X_binned_, - grad, - hess, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - ) - - # Update predictions - tree_pred = tree(self.X_binned_) - pred += self.learning_rate * tree_pred - - self.trees_.append(tree) - - # Compute losses for callbacks using actual loss function - state.train_loss = _compute_loss_value(self.loss, pred, y, quantile_alpha=self.quantile_alpha, tweedie_rho=self.tweedie_rho) - - if eval_set: - X_val, y_val = eval_set[0] - val_pred = self.predict(X_val) - state.val_loss = _compute_loss_value(self.loss, val_pred, y_val, quantile_alpha=self.quantile_alpha, tweedie_rho=self.tweedie_rho) - - # Store extra info for research callbacks - if use_goss: - state.extra['goss_sample_rate'] = sample_result.sample_rate - - # Check if callbacks want to stop - if not cb_manager.on_round_end(state): - break - - cb_manager.on_train_end(state) - - def predict(self, X: NDArray | BinnedArray) -> NDArray: - """Generate raw predictions for X. - - For regression losses (mse, mae, huber, quantile): returns predicted - values directly. - - For classification losses (logloss): returns raw logits (log-odds), - not probabilities. Use ``predict_proba()`` for class probabilities - or ``predict_label()`` for 0/1 class labels. - - Note: - This matches XGBoost's ``Booster.predict()`` behavior. The sklearn - wrapper ``OpenBoostClassifier.predict()`` returns class labels. - - Args: - X: Features to predict on, shape (n_samples, n_features). - Can be raw numpy array or pre-binned BinnedArray. - - Returns: - predictions: Shape (n_samples,). Raw scores/logits. - - Raises: - ValueError: If model is not fitted or X has wrong shape. - """ - # Check if fitted first (Phase 20.3) - if not self.trees_: - raise ValueError( - f"This {type(self).__name__} instance is not fitted yet. " - f"Call 'fit' with appropriate arguments before using 'predict'." - ) - - # Validate (Phase 20.3) - n_features = getattr(self, 'n_features_in_', None) or (self.X_binned_.n_features if self.X_binned_ else None) - if n_features is None: - raise ValueError("Model is not properly fitted. Missing feature count.") - X = validate_predict_input(self, X, n_features) - - # Bin the data if needed, using training bin edges for consistency - if isinstance(X, BinnedArray): - X_binned = X - elif self.X_binned_ is not None: - # Use transform to apply training bin edges to new data - X_binned = self.X_binned_.transform(X) - else: - X_binned = array(X, n_bins=self.n_bins) - - # Get number of samples - n_samples = X_binned.n_samples - - # Accumulate tree predictions with base score - base = getattr(self, 'base_score_', np.float32(0.0)) - - # Use GPU accumulation when data is on GPU - if is_cuda() and hasattr(X_binned.data, '__cuda_array_interface__'): - from numba import cuda - - from .._core._predict import _add_inplace_cuda, _fill_cuda - pred_gpu = cuda.device_array(n_samples, dtype=np.float32) - _fill_cuda(pred_gpu, float(base)) - for tree in self.trees_: - tree_pred = tree(X_binned) - _add_inplace_cuda(pred_gpu, tree_pred, self.learning_rate) - return pred_gpu.copy_to_host() - - pred = np.full(n_samples, base, dtype=np.float32) - for tree in self.trees_: - tree_pred = tree(X_binned) - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred += self.learning_rate * tree_pred - - return pred - - def predict_proba(self, X: NDArray | BinnedArray) -> NDArray: - """Predict class probabilities for binary classification. - - Only valid when loss='logloss'. - - Args: - X: Features to predict on. - - Returns: - probabilities: Shape (n_samples, 2) with [P(y=0), P(y=1)]. - """ - if self.loss not in ('logloss', 'binary_crossentropy'): - raise ValueError("predict_proba only available for classification losses") - - raw_pred = self.predict(X) - - # Apply sigmoid - prob_1 = 1 / (1 + np.exp(-raw_pred)) - prob_0 = 1 - prob_1 - - return np.column_stack([prob_0, prob_1]) - - def predict_label(self, X: NDArray | BinnedArray, threshold: float = 0.5) -> NDArray: - """Predict class labels for binary classification. - - Convenience method that applies sigmoid and thresholds. Only valid - when loss='logloss'. - - Args: - X: Features to predict on. - threshold: Decision threshold (default 0.5). - - Returns: - labels: Shape (n_samples,), integer values 0 or 1. - """ - proba = self.predict_proba(X) - return (proba[:, 1] >= threshold).astype(np.int32) - - -_fill_zeros_kernel = None - - -def _ensure_fill_zeros_kernel(): - """Lazily compile the fill-zeros kernel.""" - global _fill_zeros_kernel - if _fill_zeros_kernel is not None: - return - from numba import cuda - - @cuda.jit - def _kernel(arr, n): - idx = cuda.grid(1) - if idx < n: - arr[idx] = 0.0 - - _fill_zeros_kernel = _kernel - - -def _fill_zeros_gpu(arr): - """Fill GPU array with zeros.""" - _ensure_fill_zeros_kernel() - n = arr.shape[0] - threads = 256 - blocks = (n + threads - 1) // threads - _fill_zeros_kernel[blocks, threads](arr, n) - - -# ============================================================================= -# Multi-class Gradient Boosting (Phase 9.2) -# ============================================================================= - -@dataclass -class MultiClassGradientBoosting(PersistenceMixin): - """Multi-class Gradient Boosting classifier. - - Uses softmax loss and trains K trees per round (one per class), - following the XGBoost/LightGBM approach. - - Args: - n_classes: Number of classes. - n_trees: Number of boosting rounds (total trees = n_trees * n_classes). - max_depth: Maximum depth of each tree. - learning_rate: Shrinkage factor applied to each tree. - min_child_weight: Minimum sum of hessian in a leaf. - reg_lambda: L2 regularization on leaf values. - n_bins: Number of bins for histogram building. - subsample_strategy: Sampling strategy (Phase 17): 'none', 'random', 'goss'. - goss_top_rate: Fraction of top-gradient samples to keep (for GOSS). - goss_other_rate: Fraction of remaining samples to sample (for GOSS). - growth: Tree growth strategy: 'levelwise' (default), 'leafwise', or - 'symmetric'. - max_leaves: Maximum leaves per tree (used by 'leafwise' growth; - defaults to 2**max_depth when None). - - Example: - ```python - import openboost as ob - - model = ob.MultiClassGradientBoosting(n_classes=10, n_trees=100) - model.fit(X_train, y_train) # y_train: 0 to 9 - predictions = model.predict(X_test) # Returns class labels - proba = model.predict_proba(X_test) # Returns probabilities - ``` - - With GOSS sampling: - - ```python - model = ob.MultiClassGradientBoosting( - n_classes=10, n_trees=100, - subsample_strategy='goss', - goss_top_rate=0.2, - goss_other_rate=0.1 - ) - ``` - """ - - n_classes: int - n_trees: int = 100 - max_depth: int = 6 - learning_rate: float = 0.1 - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 - gamma: float = 0.0 - subsample: float = 1.0 - colsample_bytree: float = 1.0 - n_bins: int = 254 - subsample_strategy: Literal['none', 'random', 'goss'] = 'none' - goss_top_rate: float = 0.2 - goss_other_rate: float = 0.1 - batch_size: int | None = None - growth: str = 'levelwise' - max_leaves: int | None = None - - # Fitted attributes - trees_: list[list[TreeStructure]] = field(default_factory=list, init=False, repr=False) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - n_features_in_: int = field(default=0, init=False, repr=False) - - def fit( - self, - X: NDArray, - y: NDArray, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ) -> MultiClassGradientBoosting: - """Fit the multi-class gradient boosting model. - - Args: - X: Training features, shape (n_samples, n_features). - y: Training labels, shape (n_samples,). Integer class labels 0 to n_classes-1. - callbacks: List of Callback instances (e.g. EarlyStopping, Logger). - eval_set: Validation set(s) as list of (X_val, y_val) tuples. - - Returns: - self: The fitted model. - """ - from .._loss import softmax_gradient - - # Clear previous fit - self.trees_ = [] - - # Convert y to integer labels - y = np.asarray(y, dtype=np.int32).ravel() - n_samples = len(y) - - # Validate labels - if y.min() < 0 or y.max() >= self.n_classes: - raise ValueError(f"Labels must be in [0, {self.n_classes-1}], got [{y.min()}, {y.max()}]") - - # Validate growth strategy early (clear error before any training work) - if isinstance(self.growth, str): - get_growth_strategy(self.growth) - - # MED-9: Validate inputs - X = validate_X(X, allow_nan=True, context="fit") - - # Bin the data - if isinstance(X, BinnedArray): - self.X_binned_ = X - else: - self.X_binned_ = array(X, n_bins=self.n_bins) - - self.n_features_in_ = self.X_binned_.n_features - - # Initialize predictions for each class - pred = np.zeros((n_samples, self.n_classes), dtype=np.float32) - - # Setup callbacks - cb_manager = CallbackManager(callbacks) - state = TrainingState(model=self, n_rounds=self.n_trees) - cb_manager.on_train_begin(state) - - eval_set = validate_eval_set(eval_set, self.X_binned_.n_features) - warn_if_early_stopping_without_eval_set(callbacks, eval_set) - - # Determine sampling strategy (Phase 17) - use_goss = self.subsample_strategy == 'goss' - - # Train trees - for round_idx in range(self.n_trees): - # Compute softmax gradients for all classes - grad, hess = softmax_gradient(pred, y, self.n_classes) - - # Apply GOSS if enabled (Phase 17) - if use_goss: - # Use sum of absolute gradients across classes for sampling - grad_magnitude = np.sum(np.abs(grad), axis=1) - sample_result = goss_sample( - grad_magnitude, None, - top_rate=self.goss_top_rate, - other_rate=self.goss_other_rate, - seed=round_idx, - ) - sample_indices = sample_result.indices - sample_weights = sample_result.weights - else: - sample_indices = None - sample_weights = None - - # Train one tree per class - round_trees = [] - for k in range(self.n_classes): - grad_k = grad[:, k].astype(np.float32) - hess_k = hess[:, k].astype(np.float32) - - if use_goss: - # Apply GOSS sampling and weighting - # Create weighted gradient/hessian arrays - grad_k_goss = np.zeros_like(grad_k) - hess_k_goss = np.zeros_like(hess_k) - grad_k_goss[sample_indices] = grad_k[sample_indices] * sample_weights - hess_k_goss[sample_indices] = hess_k[sample_indices] * sample_weights - - tree = fit_tree( - self.X_binned_, - grad_k_goss, - hess_k_goss, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=1.0, - colsample_bytree=self.colsample_bytree, - ) - else: - tree = fit_tree( - self.X_binned_, - grad_k, - hess_k, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - growth=self.growth, - max_leaves=self.max_leaves, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - ) - round_trees.append(tree) - - # Update predictions for this class - tree_pred = tree(self.X_binned_) - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred[:, k] += self.learning_rate * tree_pred - - self.trees_.append(round_trees) - - # Callbacks - state.round_idx = round_idx - if cb_manager.callbacks: - # Cross-entropy loss - from scipy.special import log_softmax - log_probs = log_softmax(pred, axis=1) - state.train_loss = float(-np.mean( - log_probs[np.arange(n_samples), y] - )) - if eval_set: - val_proba = self.predict_proba(eval_set[0][0]) - y_val = np.asarray(eval_set[0][1], dtype=np.int32).ravel() - val_proba_clipped = np.clip(val_proba, 1e-15, 1.0) - state.val_loss = float(-np.mean( - np.log(val_proba_clipped[np.arange(len(y_val)), y_val]) - )) - if not cb_manager.on_round_end(state): - break - - cb_manager.on_train_end(state) - return self - - def predict_raw(self, X: NDArray | BinnedArray) -> NDArray: - """Get raw predictions (logits) for each class. - - Args: - X: Features to predict on. - - Returns: - logits: Shape (n_samples, n_classes). - """ - if not self.trees_: - raise RuntimeError("Model not fitted. Call fit() first.") - - # MED-9: Validate input - n_features = getattr(self, 'n_features_in_', None) or ( - self.X_binned_.n_features if self.X_binned_ else None - ) - if n_features is not None and not isinstance(X, BinnedArray): - X = validate_predict_input(self, X, n_features) - - # Bin the data if needed, using training bin edges for consistency - if isinstance(X, BinnedArray): - X_binned = X - elif self.X_binned_ is not None: - # Use transform to apply training bin edges to new data - X_binned = self.X_binned_.transform(X) - else: - X_binned = array(X, n_bins=self.n_bins) - - n_samples = X_binned.n_samples - pred = np.zeros((n_samples, self.n_classes), dtype=np.float32) - - # Accumulate predictions from all rounds - for round_trees in self.trees_: - for k, tree in enumerate(round_trees): - tree_pred = tree(X_binned) - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred[:, k] += self.learning_rate * tree_pred - - return pred - - def predict_proba(self, X: NDArray | BinnedArray) -> NDArray: - """Predict class probabilities. - - Args: - X: Features to predict on. - - Returns: - probabilities: Shape (n_samples, n_classes). - """ - logits = self.predict_raw(X) - - # Softmax - logits_max = np.max(logits, axis=1, keepdims=True) - exp_logits = np.exp(logits - logits_max) - proba = exp_logits / np.sum(exp_logits, axis=1, keepdims=True) - - return proba - - def predict(self, X: NDArray | BinnedArray) -> NDArray: - """Predict class labels. - - Args: - X: Features to predict on. - - Returns: - labels: Shape (n_samples,). Integer class labels. - """ - logits = self.predict_raw(X) - return np.argmax(logits, axis=1) diff --git a/src/openboost/_models/_dart.py b/src/openboost/_models/_dart.py deleted file mode 100644 index ab4c124..0000000 --- a/src/openboost/_models/_dart.py +++ /dev/null @@ -1,344 +0,0 @@ -"""DART: Dropouts meet Multiple Additive Regression Trees. - -Phase 8.5: Proof that the new architecture enables easy algorithm variants. - -DART is a regularization technique where random trees are dropped during -training, similar to dropout in neural networks. This prevents later trees -from simply fixing errors of earlier trees, leading to better generalization. - -Reference: - Rashmi, K. V., and Ran Gilad-Bachrach. "DART: Dropouts meet Multiple - Additive Regression Trees." AISTATS, 2015. -""" - -from __future__ import annotations - -import warnings -from dataclasses import dataclass, field -from typing import TYPE_CHECKING - -import numpy as np - -from .._array import BinnedArray, array -from .._callbacks import ( - Callback, - CallbackManager, - TrainingState, - warn_if_early_stopping_without_eval_set, -) -from .._core._growth import TreeStructure -from .._core._tree import fit_tree -from .._loss import LossFunction, get_loss_function -from .._persistence import PersistenceMixin -from .._validation import validate_eval_set -from ._boosting import _compute_loss_value - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -@dataclass -class DART(PersistenceMixin): - """DART: Gradient Boosting with Dropout. - - Implements DART (Dropouts meet Multiple Additive Regression Trees), - which randomly drops trees during training to prevent overfitting. - - Args: - n_trees: Number of trees to train. - max_depth: Maximum depth of each tree. - learning_rate: Base learning rate (shrinkage factor). - loss: Loss function ('mse', 'logloss', 'huber', or callable). - dropout_rate: Fraction of trees to drop each round (0 to 1). - skip_drop: Probability of skipping dropout for a round. - normalize: If True, normalize dropped tree contributions. - sample_type: How to sample dropped trees ('uniform' or 'weighted'). - min_child_weight: Minimum sum of hessian in a leaf. - reg_lambda: L2 regularization on leaf values. - n_bins: Number of bins for histogram building. - seed: Random seed for reproducibility. - - Example: - ```python - import openboost as ob - - # DART with 10% dropout - model = ob.DART(n_trees=100, dropout_rate=0.1) - model.fit(X_train, y_train) - predictions = model.predict(X_test) - - # DART with higher dropout for more regularization - model = ob.DART(n_trees=200, dropout_rate=0.3, skip_drop=0.5) - model.fit(X_train, y_train) - ``` - """ - - n_trees: int = 100 - max_depth: int = 6 - learning_rate: float = 0.1 - loss: str | LossFunction = 'mse' - dropout_rate: float = 0.1 - skip_drop: float = 0.0 # Probability of skipping dropout - normalize: bool = True - sample_type: str = 'uniform' # 'uniform' or 'weighted' - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - n_bins: int = 256 - seed: int | None = None - random_state: int | None = field(default=None, repr=False) - - def __post_init__(self) -> None: - # Allow random_state as alias for seed (consistency with other models) - if self.random_state is not None and self.seed is None: - self.seed = self.random_state - - # Fitted attributes (not init) - trees_: list[TreeStructure] = field(default_factory=list, init=False, repr=False) - tree_weights_: list[float] = field(default_factory=list, init=False, repr=False) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _loss_fn: LossFunction | None = field(default=None, init=False, repr=False) - _rng: np.random.Generator | None = field(default=None, init=False, repr=False) - - def fit( - self, - X: NDArray, - y: NDArray, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ) -> DART: - """Fit the DART model. - - Args: - X: Training features, shape (n_samples, n_features). - y: Training targets, shape (n_samples,). - callbacks: List of Callback instances (e.g. EarlyStopping, Logger). - eval_set: Validation set(s) as list of (X_val, y_val) tuples. - - Returns: - self: The fitted model. - """ - # Clear any previous fit - self.trees_ = [] - self.tree_weights_ = [] - - # Initialize RNG - self._rng = np.random.default_rng(self.seed) - - # Convert to float32 - y = np.asarray(y, dtype=np.float32).ravel() - n_samples = len(y) - - # Get loss function - self._loss_fn = get_loss_function(self.loss) - - # Bin the data - if isinstance(X, BinnedArray): - self.X_binned_ = X - else: - self.X_binned_ = array(X, n_bins=self.n_bins) - - # Initialize predictions with base score - if self.loss in ('logloss', 'binary_crossentropy'): - p = np.clip(np.mean(y), 1e-7, 1 - 1e-7) - self.base_score_ = np.float32(np.log(p / (1 - p))) - else: - self.base_score_ = np.float32(np.mean(y)) - pred = np.full(n_samples, self.base_score_, dtype=np.float32) - - # Setup callbacks - cb_manager = CallbackManager(callbacks) - state = TrainingState(model=self, n_rounds=self.n_trees) - cb_manager.on_train_begin(state) - - eval_set = validate_eval_set(eval_set, self.X_binned_.n_features) - warn_if_early_stopping_without_eval_set(callbacks, eval_set) - - # Train trees - for _i in range(self.n_trees): - # Decide whether to apply dropout this round - apply_dropout = ( - len(self.trees_) > 0 and - self._rng.random() >= self.skip_drop - ) - - if apply_dropout: - # Select trees to drop - dropped_indices = self._select_dropped_trees() - - # Compute predictions without dropped trees - pred_without_dropped = self._predict_without_trees( - self.X_binned_, dropped_indices - ) - - # Compute gradients against predictions without dropped trees - grad, hess = self._loss_fn(pred_without_dropped, y) - else: - # No dropout, use full predictions - dropped_indices = [] - grad, hess = self._loss_fn(pred, y) - - # Ensure float32 - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - - # Build tree using standard fit_tree (the whole point of Phase 8!) - tree = fit_tree( - self.X_binned_, - grad, - hess, - max_depth=self.max_depth, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - ) - - # Store tree with weight 1.0 — normalization is applied at - # prediction time to avoid cumulative weight corruption (HIGH-3). - self.trees_.append(tree) - self.tree_weights_.append(1.0) - - if apply_dropout and self.normalize and dropped_indices: - # For the current round's prediction update, scale the - # non-dropped trees by k/(k+1) and new tree by 1/(k+1) - # but only temporarily — don't modify stored weights. - k = len(dropped_indices) - n_samples_tmp = self.X_binned_.n_samples - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples_tmp, base, dtype=np.float32) - excluded_set = set(dropped_indices) - for t_i, (t, w) in enumerate(zip(self.trees_, self.tree_weights_, strict=False)): - t_pred = t(self.X_binned_) - if hasattr(t_pred, 'copy_to_host'): - t_pred = t_pred.copy_to_host() - if t_i == len(self.trees_) - 1: - # New tree: scale by 1/(k+1) - pred += self.learning_rate * w * (1.0 / (k + 1)) * t_pred - elif t_i in excluded_set: - # Dropped tree: re-added with normalization k/(k+1) - pred += self.learning_rate * w * (k / (k + 1)) * t_pred - else: - pred += self.learning_rate * w * t_pred - else: - # No dropout this round: full prediction from all trees - pred = self._predict_internal(self.X_binned_) - - # Callbacks - state.round_idx = _i - if cb_manager.callbacks: - state.train_loss = _compute_loss_value(self.loss, pred, y) - if eval_set: - val_pred = self.predict(eval_set[0][0]) - y_val = np.asarray(eval_set[0][1], dtype=np.float32).ravel() - state.val_loss = _compute_loss_value( - self.loss, val_pred, y_val - ) - if not cb_manager.on_round_end(state): - break - - cb_manager.on_train_end(state) - return self - - def _select_dropped_trees(self) -> list[int]: - """Select which trees to drop for this round.""" - n_trees = len(self.trees_) - if n_trees == 0: - return [] - - n_drop = max(1, int(n_trees * self.dropout_rate)) - - if self.sample_type == 'uniform': - # Uniform random selection - dropped = self._rng.choice(n_trees, size=n_drop, replace=False) - elif self.sample_type == 'weighted': - # Weight by tree contribution (not implemented, fall back to uniform) - warnings.warn( - f"sample_type='{self.sample_type}' is not implemented, " - "falling back to uniform sampling", - UserWarning, - stacklevel=2, - ) - dropped = self._rng.choice(n_trees, size=n_drop, replace=False) - else: - raise ValueError(f"Unknown sample_type: {self.sample_type}") - - return dropped.tolist() - - def _predict_without_trees( - self, - X: BinnedArray, - excluded_indices: list[int], - ) -> NDArray: - """Predict using all trees except those in excluded_indices.""" - n_samples = X.n_samples - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples, base, dtype=np.float32) - - excluded_set = set(excluded_indices) - - for i, (tree, weight) in enumerate(zip(self.trees_, self.tree_weights_, strict=False)): - if i in excluded_set: - continue - tree_pred = tree(X) - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred += self.learning_rate * weight * tree_pred - - return pred - - def _predict_internal(self, X: BinnedArray) -> NDArray: - """Internal prediction using all trees (for training).""" - n_samples = X.n_samples - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples, base, dtype=np.float32) - - for tree, weight in zip(self.trees_, self.tree_weights_, strict=False): - tree_pred = tree(X) - if hasattr(tree_pred, 'copy_to_host'): - tree_pred = tree_pred.copy_to_host() - pred += self.learning_rate * weight * tree_pred - - return pred - - def predict(self, X: NDArray | BinnedArray) -> NDArray: - """Generate predictions for X. - - Args: - X: Features to predict on, shape (n_samples, n_features). - Can be raw numpy array or pre-binned BinnedArray. - - Returns: - predictions: Shape (n_samples,). - """ - if not self.trees_: - raise RuntimeError("Model not fitted. Call fit() first.") - - # Bin the data if needed, using training bin edges for consistency - if isinstance(X, BinnedArray): - X_binned = X - elif self.X_binned_ is not None: - X_binned = self.X_binned_.transform(X) - else: - X_binned = array(X, n_bins=self.n_bins) - - return self._predict_internal(X_binned) - - def predict_proba(self, X: NDArray | BinnedArray) -> NDArray: - """Predict class probabilities for binary classification. - - Only valid when loss='logloss'. - - Args: - X: Features to predict on. - - Returns: - probabilities: Shape (n_samples, 2) with [P(y=0), P(y=1)]. - """ - if self.loss not in ('logloss', 'binary_crossentropy'): - raise ValueError("predict_proba only available for classification losses") - - raw_pred = self.predict(X) - - # Apply sigmoid - prob_1 = 1 / (1 + np.exp(-raw_pred)) - prob_0 = 1 - prob_1 - - return np.column_stack([prob_0, prob_1]) diff --git a/src/openboost/_models/_distributional.py b/src/openboost/_models/_distributional.py deleted file mode 100644 index 765fe70..0000000 --- a/src/openboost/_models/_distributional.py +++ /dev/null @@ -1,722 +0,0 @@ -"""Distributional Gradient Boosting and NGBoost. - -Phase 15.2-15.3: Probabilistic prediction via distributional regression. - -Predicts full probability distributions instead of point estimates. -Trains separate tree ensembles for each distribution parameter. - -Classes: -- DistributionalGBDT: Uses ordinary gradient descent -- NGBoost: Uses natural gradient descent (faster convergence) - -Example: - ```python - import openboost as ob - - # Standard distributional GBDT - model = ob.DistributionalGBDT(distribution='normal', n_trees=100) - model.fit(X_train, y_train) - - # Get distribution parameters - output = model.predict_distribution(X_test) - mu, sigma = output.params['loc'], output.params['scale'] - - # Get prediction intervals - lower, upper = output.interval(alpha=0.1) # 90% interval - - # Sample from predicted distribution - samples = output.sample(n_samples=100) - - # NaturalBoost (recommended) - model = ob.NaturalBoostNormal(n_trees=500) - model.fit(X_train, y_train) - ``` -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Literal - -import numpy as np - -from .._array import BinnedArray -from .._callbacks import Callback -from .._core._growth import TreeStructure -from .._distributions import ( - Distribution, - DistributionOutput, - Normal, - get_distribution, -) -from .._objectives import DistributionObjective -from .._persistence import PersistenceMixin -from .._trainer import TrainerConfig, fit_boosting, predict_raw -from .._utils import crps_empirical, crps_gaussian, interval_score, pinball_loss -from .._validation import validate_sample_weight - -if TYPE_CHECKING: - from numpy.typing import NDArray - -#: Metrics accepted by ``fit(eval_metric=...)``. -EVAL_METRICS = ('nll', 'crps', 'pinball', 'interval_score') - - -def _validate_exposure(exposure, n_samples: int, context: str = "fit") -> NDArray: - """Validate an exposure vector: positive, finite, shape (n_samples,). - - Scalars are broadcast to all samples. - """ - exposure = np.asarray(exposure, dtype=np.float64) - if exposure.ndim == 0: - exposure = np.full(n_samples, float(exposure), dtype=np.float64) - if exposure.ndim != 1 or len(exposure) != n_samples: - raise ValueError( - f"exposure must be a scalar or 1D array of length {n_samples} " - f"(matching X in {context}), got shape {exposure.shape}." - ) - if not np.all(np.isfinite(exposure)) or np.any(exposure <= 0): - raise ValueError("exposure must contain strictly positive finite values.") - return exposure.astype(np.float32) - - -def _split_eval_exposures(eval_set): - """Split optional 3-tuple ``(X, y, exposure)`` eval entries. - - Returns ``(pairs, exposures)`` where ``pairs`` is a list of ``(X, y)`` - tuples (ready for ``validate_eval_set``) and ``exposures`` the aligned - list of per-set exposure vectors (None where not given). Mirrors - ``validate_eval_set``'s auto-wrap of a single bare tuple. - """ - if eval_set is None: - return None, None - if ( - isinstance(eval_set, tuple) - and len(eval_set) in (2, 3) - and (hasattr(eval_set[0], 'shape') or hasattr(eval_set[0], '__len__')) - and not isinstance(eval_set[0], tuple) - ): - eval_set = [eval_set] - - pairs, exposures = [], [] - for item in eval_set: - if isinstance(item, tuple) and len(item) == 3: - pairs.append((item[0], item[1])) - exposures.append(item[2]) - else: - # 2-tuples (and malformed items, which validate_eval_set rejects - # with its standard message) pass through unchanged - pairs.append(item) - exposures.append(None) - return pairs, exposures - - -@dataclass -class DistributionalGBDT(PersistenceMixin): - """Distributional Gradient Boosting for probabilistic prediction. - - Trains K tree ensembles, where K = number of distribution parameters. - Each ensemble predicts one parameter (e.g., mean, variance). - Uses ordinary gradient descent. - - For faster convergence, consider using NGBoost (natural gradient). - - Args: - distribution: Distribution name ('normal', 'gamma', 'poisson', etc.) - or Distribution instance - n_trees: Number of boosting rounds - max_depth: Maximum depth of each tree - learning_rate: Shrinkage factor applied to each tree - min_child_weight: Minimum sum of hessian in a leaf - reg_lambda: L2 regularization on leaf values - reg_alpha: L1 regularization on leaf values - subsample: Row sampling ratio (0.0-1.0) - colsample_bytree: Column sampling ratio (0.0-1.0) - n_bins: Number of bins for histogram building - - Attributes: - trees_: Dict mapping param_name -> list of trees - distribution_: Fitted Distribution instance - evals_result_: Per-round eval-set metric history recorded during - fit(), e.g. ``{'eval_0': {'nll': [...]}, 'eval_1': {'nll': [...]}}`` - best_iteration_: Best round index (set when early stopping is used) - best_score_: Best monitored metric value (set with best_iteration_) - - Example: - ```python - model = DistributionalGBDT(distribution='normal', n_trees=100) - model.fit(X_train, y_train) - - # Point prediction (mean) - y_pred = model.predict(X_test) - - # Full distribution - output = model.predict_distribution(X_test) - lower, upper = output.interval(alpha=0.1) - ``` - """ - - distribution: ( - Literal['normal', 'lognormal', 'gamma', 'poisson', 'studentt', 'tweedie', 'negbin'] - | Distribution - ) = 'normal' - n_trees: int = 100 - max_depth: int = 6 - learning_rate: float = 0.1 - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 - subsample: float = 1.0 - colsample_bytree: float = 1.0 - n_bins: int = 254 - - # Fitted attributes (not init) - trees_: dict[str, list[TreeStructure]] = field(default_factory=dict, init=False, repr=False) - distribution_: Distribution | None = field(default=None, init=False, repr=False) - evals_result_: dict[str, dict[str, list[float]]] = field( - default_factory=dict, init=False, repr=False - ) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _base_scores: dict[str, float] = field(default_factory=dict, init=False, repr=False) - n_features_in_: int = field(default=0, init=False, repr=False) - _use_natural_gradient: bool = field(default=False, init=False, repr=False) - - def fit( - self, - X: NDArray, - y: NDArray, - sample_weight: NDArray | None = None, - exposure: NDArray | None = None, - callbacks: list[Callback] | None = None, - eval_set: list[tuple] | None = None, - eval_metric: Literal['nll', 'crps', 'pinball', 'interval_score'] = 'nll', - quantiles: list[float] | None = None, - interval_alpha: float = 0.1, - early_stopping_rounds: int | None = None, - ) -> DistributionalGBDT: - """Fit the distributional gradient boosting model. - - Args: - X: Training features, shape (n_samples, n_features) - y: Training targets, shape (n_samples,) - sample_weight: Optional non-negative per-sample weights ``w_i``, - shape (n_samples,). The training objective becomes - ``sum_i w_i * NLL_i`` and the reported train loss is the - weighted mean NLL. - exposure: Optional strictly positive per-sample exposure ``e_i`` - (scalar or shape (n_samples,)) for families with a log-link - mean (Poisson, NegativeBinomial, Gamma, Tweedie): the mean is - modeled as ``mu_i = e_i * exp(raw_mu_i)``, i.e. ``log(e_i)`` - is added as an offset to the mean parameter's raw score. - Raises ValueError for families without a log-link mean - (Normal, LogNormal, StudentT). - callbacks: List of Callback instances (e.g. EarlyStopping, Logger). - eval_set: Validation set(s) as a list of ``(X_val, y_val)`` - tuples. Entries may also be 3-tuples - ``(X_val, y_val, exposure_val)`` for exposure-aware - validation of log-link-mean families. Every eval set is - evaluated each round and the per-round history is stored in - ``evals_result_``. - eval_metric: Metric computed on each eval set every round: - 'nll' (default), 'crps' (closed form for Normal, otherwise - estimated from 100 fixed-seed Monte Carlo samples per round), - 'pinball' (mean pinball loss over ``quantiles``), or - 'interval_score' (central interval with miscoverage - ``interval_alpha``). - quantiles: Quantile levels used by eval_metric='pinball'. - Defaults to ``[0.05, 0.5, 0.95]``. - interval_alpha: Miscoverage rate used by - eval_metric='interval_score' (0.1 scores the central 90% - interval). - early_stopping_rounds: Stop training when the LAST eval set's - metric has not improved for this many consecutive rounds. - The model is restored to (truncated at) the best iteration - and ``best_iteration_`` / ``best_score_`` are set. - - Returns: - self: Fitted model - """ - # Get distribution instance - self.distribution_ = get_distribution(self.distribution) - - y = np.asarray(y, dtype=np.float32).ravel() - n_samples = len(y) - - sample_weight = validate_sample_weight(sample_weight, n_samples) - - if eval_metric not in EVAL_METRICS: - raise ValueError( - f"Unknown eval_metric '{eval_metric}'. " - f"Available: {', '.join(EVAL_METRICS)}." - ) - - eval_pairs, eval_exposures = _split_eval_exposures(eval_set) - - exposure_param: str | None = None - exposure_sign = 0.0 - needs_exposure = exposure is not None or any( - e is not None for e in (eval_exposures or []) - ) - if needs_exposure: - exposure_param, exposure_sign = self._resolve_exposure_offset() - - train_log_offset = None - if exposure is not None: - exposure = _validate_exposure(exposure, n_samples, context="fit") - train_log_offset = (exposure_sign * np.log(exposure)).astype(np.float32) - - objective = DistributionObjective( - self.distribution_, - natural=self._use_natural_gradient, - exposure_param=exposure_param, - exposure_sign=exposure_sign, - ) - - eval_sets = None - if eval_pairs: - eval_sets = [] - for (X_e, y_e), exp_e in zip(eval_pairs, eval_exposures, strict=True): - extra_e: dict = {} - if exp_e is not None: - y_e_arr = np.asarray(y_e).ravel() - exp_e = _validate_exposure(exp_e, len(y_e_arr), context="eval") - extra_e["log_offset"] = ( - exposure_sign * np.log(exp_e) - ).astype(np.float32) - eval_sets.append({"X": X_e, "y": y_e, "extra": extra_e}) - - def _score_eval(y_e, raw_e, extra_e): - params_e = objective.constrain(raw_e, extra_e) - return self._eval_metric_value( - y_e, params_e, eval_metric, quantiles, interval_alpha - ) - - fit_boosting( - self, - objective, - X, - y, - config=TrainerConfig( - n_trees=self.n_trees, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - n_bins=self.n_bins, - ), - sample_weight=sample_weight, - extra={"log_offset": train_log_offset}, - callbacks=callbacks, - early_stopping_rounds=early_stopping_rounds, - eval_sets=eval_sets, - eval_fn=_score_eval if eval_sets else None, - eval_metric_name=eval_metric, - ) - return self - - def _resolve_exposure_offset(self) -> tuple[str, float]: - """Resolve (and verify) the raw parameter carrying the log-exposure offset. - - Returns: - (param_name, sign) such that adding ``sign * log(e)`` to that - parameter's raw score multiplies the distribution mean by ``e``. - - Raises: - ValueError: If the family has no log-link mean (exposure unsupported). - """ - dist = self.distribution_ - info = dist.exposure_offset - if info is None: - raise ValueError( - f"exposure is not supported for {type(dist).__name__}" - ) - name, sign = info - - # Verify (rather than trust) the declared offset against the actual - # Distribution object: the offset parameter must have a multiplicative - # (log) link, and shifting its raw score by sign*log(k) must multiply - # the mean by k. Two baselines guard against coincidental matches. - k = 2.0 - c = float(np.log(k)) - for base in (0.25, -0.4): - raws = {p: np.array([base]) for p in dist.param_names} - params0 = {p: dist.link(p, raws[p]) for p in dist.param_names} - raws_off = dict(raws) - raws_off[name] = raws[name] + sign * c - params1 = {p: dist.link(p, raws_off[p]) for p in dist.param_names} - link_multiplicative = np.allclose( - params1[name], params0[name] * k ** sign, rtol=1e-5 - ) - mean_scales = np.allclose( - dist.mean(params1), k * dist.mean(params0), rtol=1e-5 - ) - if not (link_multiplicative and mean_scales): - raise ValueError( - f"exposure is not supported for {type(dist).__name__}: " - f"declared exposure_offset {info} failed verification " - "(offset does not scale the mean multiplicatively)." - ) - return name, float(sign) - - def _constrained_params( - self, - raw_preds: dict[str, NDArray], - exposure_param: str | None = None, - log_offset: NDArray | None = None, - ) -> dict[str, NDArray]: - """Apply link functions (plus optional log-exposure offset) to raw scores.""" - params = {} - for p in self.distribution_.param_names: - raw = raw_preds[p] - if log_offset is not None and p == exposure_param: - raw = raw + log_offset - params[p] = self.distribution_.link(p, raw) - return params - - def _eval_metric_value( - self, - y: NDArray, - params: dict[str, NDArray], - metric: str, - quantiles: list[float] | None, - interval_alpha: float, - ) -> float: - """Compute one eval-set metric value from constrained parameters.""" - if metric == 'nll': - return float(np.mean(self.distribution_.nll(y, params))) - if metric == 'crps': - if isinstance(self.distribution_, Normal): - return crps_gaussian(y, params['loc'], params['scale']) - # Non-Gaussian families: empirical CRPS from Monte Carlo samples. - # Fixed seed keeps per-round values comparable across rounds. - samples = self.distribution_.sample(params, n_samples=100, seed=0) - return crps_empirical(y, samples) - if metric == 'pinball': - qs = list(quantiles) if quantiles is not None else [0.05, 0.5, 0.95] - losses = [ - pinball_loss(y, self.distribution_.quantile(params, q), quantile=q) - for q in qs - ] - return float(np.mean(losses)) - if metric == 'interval_score': - lower = self.distribution_.quantile(params, interval_alpha / 2) - upper = self.distribution_.quantile(params, 1 - interval_alpha / 2) - return interval_score(y, lower, upper, alpha=interval_alpha) - raise ValueError(f"Unknown eval_metric '{metric}'.") # pragma: no cover - - def predict_params( - self, - X: NDArray | BinnedArray, - exposure: NDArray | None = None, - ) -> dict[str, NDArray]: - """Predict distribution parameters. - - Args: - X: Features to predict on - exposure: Optional strictly positive per-sample exposure (scalar - or shape (n_samples,)) for log-link-mean families: the mean - parameter becomes ``e_i * exp(raw_mu_i)``. Default None - (exposure 1). Raises ValueError for families without a - log-link mean. - - Returns: - Dictionary mapping param_name -> predicted values - (in constrained parameter space) - """ - raw_preds = predict_raw(self, X) - - if exposure is not None: - name, sign = self._resolve_exposure_offset() - n = next(iter(raw_preds.values())).shape[0] - exposure = _validate_exposure(exposure, n, context="predict") - log_offset = (sign * np.log(exposure)).astype(np.float32) - return self._constrained_params(raw_preds, name, log_offset) - - return self._constrained_params(raw_preds) - - def predict_distribution( - self, - X: NDArray | BinnedArray, - exposure: NDArray | None = None, - ) -> DistributionOutput: - """Predict full distribution. - - Args: - X: Features to predict on - exposure: Optional per-sample exposure (see ``predict_params``) - - Returns: - DistributionOutput with params, mean(), variance(), - quantile(), interval(), sample() methods - """ - params = self.predict_params(X, exposure=exposure) - return DistributionOutput(params=params, distribution=self.distribution_) - - def predict( - self, - X: NDArray | BinnedArray, - exposure: NDArray | None = None, - ) -> NDArray: - """Predict mean (expected value). - - This provides a point prediction for compatibility with standard GBDT. - - Args: - X: Features to predict on - exposure: Optional per-sample exposure (see ``predict_params``) - - Returns: - Predicted mean values - """ - params = self.predict_params(X, exposure=exposure) - return self.distribution_.mean(params) - - def predict_interval( - self, - X: NDArray | BinnedArray, - alpha: float = 0.1, - exposure: NDArray | None = None, - ) -> tuple[NDArray, NDArray]: - """Predict (1-alpha) prediction interval. - - Args: - X: Features to predict on - alpha: Significance level (0.1 = 90% interval) - exposure: Optional per-sample exposure (see ``predict_params``) - - Returns: - (lower, upper) bounds - """ - output = self.predict_distribution(X, exposure=exposure) - return output.interval(alpha) - - def predict_quantile( - self, - X: NDArray | BinnedArray, - q: float, - exposure: NDArray | None = None, - ) -> NDArray: - """Predict q-th quantile. - - Args: - X: Features to predict on - q: Quantile level (0 < q < 1) - exposure: Optional per-sample exposure (see ``predict_params``) - - Returns: - Predicted quantiles - """ - output = self.predict_distribution(X, exposure=exposure) - return output.quantile(q) - - def sample( - self, - X: NDArray | BinnedArray, - n_samples: int = 1, - seed: int | None = None, - exposure: NDArray | None = None, - ) -> NDArray: - """Sample from predicted distribution. - - Args: - X: Features, shape (n, n_features) - n_samples: Number of samples per observation - seed: Random seed for reproducibility - exposure: Optional per-sample exposure (see ``predict_params``) - - Returns: - samples: Shape (n, n_samples) - """ - output = self.predict_distribution(X, exposure=exposure) - return output.sample(n_samples, seed) - - def score( - self, - X: NDArray | BinnedArray, - y: NDArray, - exposure: NDArray | None = None, - ) -> float: - """Compute negative log-likelihood (lower is better). - - Args: - X: Features - y: True target values - exposure: Optional per-sample exposure (see ``predict_params``) - - Returns: - Mean negative log-likelihood - """ - output = self.predict_distribution(X, exposure=exposure) - nll = output.nll(np.asarray(y, dtype=np.float32)) - return float(np.mean(nll)) - - def nll( - self, - X: NDArray | BinnedArray, - y: NDArray, - exposure: NDArray | None = None, - ) -> float: - """Alias for score() - compute mean NLL.""" - return self.score(X, y, exposure=exposure) - - def _post_load(self) -> None: - """Recreate distribution instance after loading from file.""" - if self.distribution_ is None and self.distribution is not None: - self.distribution_ = get_distribution(self.distribution) - - -@dataclass -class NaturalBoost(DistributionalGBDT): - """Natural Gradient Boosting for probabilistic prediction. - - OpenBoost's implementation of natural gradient boosting, inspired by NGBoost. - Uses natural gradient instead of ordinary gradient, leading to faster - convergence by accounting for the geometry of the parameter space. - - Natural gradient: F^{-1} @ ordinary_gradient - where F is the Fisher information matrix. - - Key advantages over standard GBDT: - - Full probability distributions, not just point estimates - - Prediction intervals and uncertainty quantification - - Faster convergence than ordinary gradient descent - - Key advantages over official NGBoost: - - GPU acceleration via histogram-based trees - - Faster on large datasets (>10k samples) - - Custom distributions with autodiff support - - Reference: - Duan et al. "NGBoost: Natural Gradient Boosting for Probabilistic - Prediction." ICML 2020. - - Args: - distribution: Distribution name or instance - n_trees: Number of boosting rounds (often needs fewer than ordinary) - max_depth: Maximum depth of each tree (default 4, often smaller is better) - learning_rate: Shrinkage factor - min_child_weight: Minimum sum of hessian in a leaf - reg_lambda: L2 regularization - n_bins: Number of bins for histogram building - - Example: - ```python - model = NaturalBoost(distribution='normal', n_trees=500) - model.fit(X_train, y_train) - - # Get prediction intervals - lower, upper = model.predict_interval(X_test, alpha=0.1) - - # Get full distribution - output = model.predict_distribution(X_test) - samples = output.sample(n_samples=1000) - ``` - """ - - # Override defaults for NaturalBoost - max_depth: int = 4 # Shallower trees often work better - learning_rate: float = 0.1 - _use_natural_gradient: bool = field(default=True, init=False, repr=False) - - -# ============================================================================= -# Convenience aliases -# ============================================================================= - -# NaturalBoost with specific distributions -def NaturalBoostNormal(**kwargs) -> NaturalBoost: - """NaturalBoost with Normal distribution.""" - return NaturalBoost(distribution='normal', **kwargs) - - -def NaturalBoostLogNormal(**kwargs) -> NaturalBoost: - """NaturalBoost with LogNormal distribution (for positive data).""" - return NaturalBoost(distribution='lognormal', **kwargs) - - -def NaturalBoostGamma(**kwargs) -> NaturalBoost: - """NaturalBoost with Gamma distribution (for positive data).""" - return NaturalBoost(distribution='gamma', **kwargs) - - -def NaturalBoostPoisson(**kwargs) -> NaturalBoost: - """NaturalBoost with Poisson distribution (for count data).""" - return NaturalBoost(distribution='poisson', **kwargs) - - -def NaturalBoostStudentT(**kwargs) -> NaturalBoost: - """NaturalBoost with Student-t distribution (for heavy-tailed data).""" - return NaturalBoost(distribution='studentt', **kwargs) - - -# ============================================================================= -# Kaggle Competition Favorites -# ============================================================================= - -def NaturalBoostTweedie(power: float = 1.5, **kwargs) -> NaturalBoost: - """NaturalBoost with Tweedie distribution (for insurance claims, zero-inflated data). - - **Kaggle Use Cases**: - - Porto Seguro Safe Driver Prediction - - Allstate Claims Severity - - Any zero-inflated positive target - - Args: - power: Tweedie power parameter (1 < power < 2). - 1.5 is the default for insurance claims. - **kwargs: Other NaturalBoost parameters (n_trees, learning_rate, etc.) - - Example: - ```python - model = NaturalBoostTweedie(power=1.5, n_trees=500) - model.fit(X_train, y_train) # y has zeros and positive values - - # Get prediction intervals (XGBoost can't do this!) - lower, upper = model.predict_interval(X_test, alpha=0.1) - ``` - """ - from .._distributions import Tweedie - return NaturalBoost(distribution=Tweedie(power=power), **kwargs) - - -def NaturalBoostNegBin(**kwargs) -> NaturalBoost: - """NaturalBoost with Negative Binomial distribution (for overdispersed count data). - - **Kaggle Use Cases**: - - Rossmann Store Sales - - Bike Sharing Demand - - Grupo Bimbo Inventory Demand - - Any count prediction where variance > mean - - Args: - **kwargs: NaturalBoost parameters (n_trees, learning_rate, etc.) - - Example: - ```python - model = NaturalBoostNegBin(n_trees=500) - model.fit(X_train, y_train) # y is count data - - # Probability of exceeding threshold (demand planning!) - output = model.predict_distribution(X_test) - prob_high_demand = output.distribution.prob_exceed(output.params, 100) - ``` - """ - return NaturalBoost(distribution='negativebinomial', **kwargs) - - -# ============================================================================= -# Backward compatibility aliases (deprecated) -# ============================================================================= - -# Keep old names working but mark as deprecated -NGBoost = NaturalBoost # Alias for backward compatibility -NGBoostNormal = NaturalBoostNormal -NGBoostLogNormal = NaturalBoostLogNormal -NGBoostGamma = NaturalBoostGamma -NGBoostPoisson = NaturalBoostPoisson -NGBoostStudentT = NaturalBoostStudentT -NGBoostTweedie = NaturalBoostTweedie -NGBoostNegBin = NaturalBoostNegBin diff --git a/src/openboost/_models/_formula.py b/src/openboost/_models/_formula.py deleted file mode 100644 index faf1ca5..0000000 --- a/src/openboost/_models/_formula.py +++ /dev/null @@ -1,183 +0,0 @@ -"""FormulaBoost: boost every parameter of a user formula. - -Varying-coefficient model. Features ``Z`` determine -parameter surfaces ``theta(Z)`` via trees; a user formula -``y ≈ f(theta, x)`` consumes those parameters and a structural input ``x``. -""" - -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass, field -from typing import Any - -import numpy as np -from numpy.typing import NDArray - -from .._array import BinnedArray -from .._callbacks import Callback -from .._core._growth import TreeStructure -from .._objectives import FormulaObjective -from .._persistence import PersistenceMixin -from .._trainer import TrainerConfig, fit_boosting, predict_raw -from .._validation import validate_1d - - -@dataclass -class FormulaBoost(PersistenceMixin): - """Boost the parameters of an arbitrary differentiable formula. - - Args: - formula: ``formula(theta, x) -> yhat``. ``theta`` is a tuple of K - arrays in constrained parameter space; ``x`` is the structural - input (e.g. spend, dose). - n_params: Number of formula parameters K. - links: Per-parameter link (``identity``, ``log``, ``softplus``, - ``sigmoid``), length K. - loss: Training loss. Currently ``mse`` only. - precond: GGN preconditioner: ``full`` (default), ``diag``, or - ``plain`` (raw gradient, usually a bad idea). - damp: Levenberg–Marquardt damping added to the GGN matrix. - param_names: Optional names for the K parameters. Defaults to - ``theta_0``, ``theta_1``, ... - - Tree knobs (``n_trees``, ``max_depth``, ``learning_rate``, ...) match - ``GradientBoosting``. - - Example: - ```python - def curve(theta, x): - a, b = theta - return a * x ** (1.0 / (1.0 + np.exp(-b * x))) - - m = FormulaBoost( - formula=curve, n_params=2, links=("log", "identity"), - param_names=("a", "b"), - ) - m.fit(Z, y, model_input=x) - params = m.predict_params(Z) # per-sample (a, b) - yhat = m.predict(Z, model_input=x_new) - ``` - """ - - formula: Callable - n_params: int - links: tuple[str, ...] - loss: str = "mse" - precond: str = "full" - damp: float = 1.0 - param_names: tuple[str, ...] | None = None - n_trees: int = 100 - max_depth: int = 3 - learning_rate: float = 0.1 - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 - subsample: float = 1.0 - colsample_bytree: float = 1.0 - n_bins: int = 254 - - trees_: dict[str, list[TreeStructure]] = field( - default_factory=dict, init=False, repr=False - ) - evals_result_: dict[str, dict[str, list[float]]] = field( - default_factory=dict, init=False, repr=False - ) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _base_scores: dict[str, float] = field(default_factory=dict, init=False, repr=False) - n_features_in_: int = field(default=0, init=False, repr=False) - _objective: FormulaObjective | None = field(default=None, init=False, repr=False) - - def _make_objective(self) -> FormulaObjective: - return FormulaObjective( - self.formula, - self.n_params, - self.links, - loss=self.loss, - precond=self.precond, - damp=self.damp, - param_names=self.param_names, - ) - - def fit( - self, - X: NDArray, - y: NDArray, - model_input: NDArray, - sample_weight: NDArray | None = None, - callbacks: list[Callback] | None = None, - eval_set: list[tuple] | None = None, - early_stopping_rounds: int | None = None, - ) -> FormulaBoost: - """Fit parameter surfaces ``theta(Z)`` from ``(X, y, model_input)``. - - ``eval_set`` entries are ``(X_val, y_val, model_input_val)``. - """ - y = np.asarray(y, dtype=np.float64).ravel() - x = validate_1d(model_input, len(y), "model_input") - self._objective = self._make_objective() - - eval_sets: list[dict[str, Any]] | None = None - if eval_set is not None: - if ( - isinstance(eval_set, tuple) - and len(eval_set) == 3 - and not isinstance(eval_set[0], tuple) - ): - eval_set = [eval_set] - eval_sets = [] - for item in eval_set: - if not (isinstance(item, tuple) and len(item) == 3): - raise ValueError( - "FormulaBoost eval_set entries must be " - "(X_val, y_val, model_input_val)." - ) - X_e, y_e, x_e = item - y_e = np.asarray(y_e, dtype=np.float64).ravel() - eval_sets.append( - { - "X": X_e, - "y": y_e, - "extra": {"model_input": validate_1d(x_e, len(y_e), "model_input")}, - } - ) - - fit_boosting( - self, - self._objective, - X, - y, - config=TrainerConfig( - n_trees=self.n_trees, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - n_bins=self.n_bins, - ), - sample_weight=sample_weight, - extra={"model_input": x}, - callbacks=callbacks, - early_stopping_rounds=early_stopping_rounds, - eval_sets=eval_sets, - eval_metric_name="mse", - ) - return self - - def predict_params(self, X: NDArray | BinnedArray) -> dict[str, NDArray]: - """Predict constrained formula parameters for each row of ``X``.""" - if self._objective is None: - self._objective = self._make_objective() - raw = predict_raw(self, X) - return self._objective.constrain(raw) - - def predict(self, X: NDArray | BinnedArray, model_input: NDArray) -> NDArray: - """Evaluate the formula at ``predict_params(X)`` and ``model_input``.""" - params = self.predict_params(X) - names = self._objective.channel_names - x = validate_1d(model_input, next(iter(params.values())).shape[0], "model_input") - theta = tuple(params[name] for name in names) - return np.asarray(self.formula(theta, x), dtype=np.float64).ravel() diff --git a/src/openboost/_models/_gam.py b/src/openboost/_models/_gam.py deleted file mode 100644 index 44c2522..0000000 --- a/src/openboost/_models/_gam.py +++ /dev/null @@ -1,1035 +0,0 @@ -"""GPU-accelerated Generalized Additive Model (GAM). - -This implements an EBM-style model that's fully GPU-parallelized: -- Shape functions learned via gradient boosting -- All features updated in parallel each round -- Inherently interpretable (each feature has a 1D lookup table) - -Unlike InterpretML's EBM which trains features sequentially (CPU-bound), -this trains all feature shape functions simultaneously on GPU. -""" - -from __future__ import annotations - -import warnings -from dataclasses import dataclass, field -from typing import TYPE_CHECKING - -import numpy as np - -from .._array import BinnedArray, array -from .._backends import is_cuda -from .._callbacks import ( - Callback, - CallbackManager, - EarlyStopping, - TrainingState, - warn_if_early_stopping_without_eval_set, -) -from .._loss import LossFunction, compute_loss_value, get_loss_function -from .._persistence import PersistenceMixin -from .._validation import validate_eval_set - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -@dataclass -class OpenBoostGAM(PersistenceMixin): - """GPU-accelerated Generalized Additive Model. - - An interpretable model where: - `prediction = sum(shape_function[i](feature[i]) for all features)` - - Each shape function is a lookup table mapping binned feature values - to contribution scores. Trained via parallel gradient boosting. - - Args: - n_rounds: Number of boosting rounds. - learning_rate: Shrinkage factor (smaller = more stable, needs more rounds). - reg_lambda: L2 regularization on leaf values. - loss: Loss function ('mse', 'logloss', or callable). - n_bins: Number of bins for histogram building (2-256). Binned data is - uint8 with bin 255 reserved for missing values, so at most 254 - usable bins; 255/256 are clamped to 254 by ``ob.array``. Shape - function tables are always allocated 256-wide so every uint8 bin - index (including the missing-value bin) stays in bounds. - interactions: Number of pairwise interaction terms (GA2M-style) to - learn after main-effects training. 0 (default) disables - interactions and preserves the exact pre-existing behavior. - Candidate pairs are ranked FAST-style: a one-shot 2D histogram - Newton step is scored on the main-effects residual gradients for - every feature pair (rows are subsampled for ranking on large - datasets), the top-``interactions`` pairs are selected, and 2D - shape tables on the (bin_i, bin_j) grid are then boosted with the - same Newton update and ``reg_lambda``. - interaction_rounds: Boosting rounds for the interaction stage. - None (default) uses ``n_rounds``. - smoothing: Fused-ridge smoothing strength for 1D shape functions - (default 0.0 = off, exact pre-existing behavior). Each round's - per-feature Newton update ``u`` is replaced by the solution of the - tridiagonal system ``(W + smoothing * D^T D) s = W u`` where ``D`` - is the first-difference matrix and ``W`` is a 0/1 diagonal marking - bins that contain data. Occupied bins anchor the solution while - empty bins interpolate between their neighbors, damping - sparse-bin noise. Applies to ordinal (numeric) bins only: - categorical features, the missing-value bin (255), and 2D - interaction tables are not smoothed. - monotone: Optional dict mapping feature index -> +1 (non-decreasing) - or -1 (non-increasing). After every boosting round the feature's - accumulated 1D shape function is projected onto the constraint - with weighted isotonic regression (PAVA, weighted by per-bin - sample counts). Applies to ordinal bins only; the missing-value - bin and 2D interaction tables are unconstrained. Default None - (no constraints, exact pre-existing behavior). - - Note: - The interaction stage, smoothing, monotone projection, and the - callbacks/eval_set machinery all run on CPU. With a CUDA backend, - plain fits keep the GPU main-effects path (interaction boosting then - runs on CPU afterwards); fits that request smoothing, monotone - constraints, callbacks, or eval_set fall back to CPU training with a - warning. - - Example: - ```python - import openboost as ob - - gam = ob.OpenBoostGAM(n_rounds=1000, learning_rate=0.01) - gam.fit(X_train, y_train) - predictions = gam.predict(X_test) - - # Interpret: plot shape function for feature 0 - gam.plot_shape_function(0, feature_name="age") - ``` - """ - - n_rounds: int = 1000 - learning_rate: float = 0.01 - reg_lambda: float = 1.0 - loss: str | LossFunction = 'mse' - n_bins: int = 254 - n_trees: int | None = field(default=None, repr=False) - interactions: int = 0 - interaction_rounds: int | None = None - smoothing: float = 0.0 - monotone: dict[int, int] | None = None - - def __post_init__(self) -> None: - # uint8 binning contract: at most 254 usable bins, bin 255 reserved for - # missing values. 256 stays accepted as "maximum resolution" (the - # historical default; ob.array clamps it to 254); anything outside - # [2, 256] cannot be represented and fails fast here. - if not 2 <= self.n_bins <= 256: - raise ValueError( - f"n_bins must be in [2, 256] (uint8 binning: at most 254 usable bins, " - f"bin 255 reserved for missing values); got {self.n_bins}" - ) - if self.interactions < 0: - raise ValueError(f"interactions must be >= 0; got {self.interactions}") - if self.interaction_rounds is not None and self.interaction_rounds < 0: - raise ValueError( - f"interaction_rounds must be None or >= 0; got {self.interaction_rounds}" - ) - if self.smoothing < 0: - raise ValueError(f"smoothing must be >= 0; got {self.smoothing}") - if self.monotone: - for f_idx, direction in self.monotone.items(): - if direction not in (-1, 1): - raise ValueError( - f"monotone[{f_idx}] must be +1 (non-decreasing) or " - f"-1 (non-increasing); got {direction}" - ) - if self.n_trees is not None: - if self.n_rounds != 1000: - warnings.warn( - "Both n_trees and n_rounds specified. Using n_trees.", - UserWarning, - stacklevel=2, - ) - self.n_rounds = self.n_trees - - # Fitted attributes - shape_values_: NDArray | None = field(default=None, init=False, repr=False) - pair_shape_values_: dict[tuple[int, int], NDArray] = field( - default_factory=dict, init=False, repr=False - ) - interaction_pairs_: list[tuple[int, int]] = field( - default_factory=list, init=False, repr=False - ) - evals_result_: dict[str, dict[str, list[float]]] = field( - default_factory=dict, init=False, repr=False - ) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _loss_fn: LossFunction | None = field(default=None, init=False, repr=False) - - @property - def trees_(self) -> dict[str, object]: - """Snapshot adapter for the shared callback machinery. - - ``EarlyStopping(restore_best=True)`` (see ``_callbacks.py``, which is - shared across all models and must not be edited here) snapshots - ``model.trees_`` with ``copy.deepcopy`` whenever the validation metric - improves and assigns the best snapshot back at train end. A GAM has no - tree list; its learnable state is the 1D/2D lookup tables. Exposing - that state through a ``trees_`` property (with a setter that restores - it) makes restore-to-best work for GAM without touching the callback - code. This property is not a dataclass field and never appears in - ``vars(self)``, so persistence ignores it. - """ - return { - 'shape_values': self.shape_values_, - 'pair_shape_values': self.pair_shape_values_, - 'interaction_pairs': self.interaction_pairs_, - } - - @trees_.setter - def trees_(self, snapshot: dict[str, object]) -> None: - self.shape_values_ = snapshot['shape_values'] - self.pair_shape_values_ = snapshot['pair_shape_values'] - self.interaction_pairs_ = snapshot['interaction_pairs'] - - def _post_load(self) -> None: - """Backfill attributes missing from models saved before interactions.""" - if not hasattr(self, 'pair_shape_values_') or self.pair_shape_values_ is None: - self.pair_shape_values_ = {} - if not hasattr(self, 'interaction_pairs_') or self.interaction_pairs_ is None: - self.interaction_pairs_ = [] - if not hasattr(self, 'evals_result_') or self.evals_result_ is None: - self.evals_result_ = {} - # Restore tuple keys: joblib/pickle keeps them intact, but be robust - # to states where keys were stored as lists. - self.pair_shape_values_ = { - tuple(k): v for k, v in self.pair_shape_values_.items() - } - self.interaction_pairs_ = [tuple(p) for p in self.interaction_pairs_] - - def fit( - self, - X: NDArray, - y: NDArray, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - early_stopping_rounds: int | None = None, - ) -> OpenBoostGAM: - """Fit the GAM model. - - Args: - X: Training features, shape (n_samples, n_features). - y: Training targets, shape (n_samples,). - callbacks: List of Callback instances for training hooks - (e.g. ``EarlyStopping``, ``Logger``, ``HistoryCallback``), - mirroring ``GradientBoosting.fit``. - eval_set: Validation set(s) as a list of ``(X_val, y_val)`` - tuples (a single tuple is auto-wrapped). Every eval set is - evaluated each round with the training loss as the metric and - the per-round history is stored in ``evals_result_`` as - ``{'eval_0': {'': [...]}, ...}``. Early stopping - monitors the LAST eval set. - early_stopping_rounds: Convenience for - ``EarlyStopping(patience=early_stopping_rounds, - restore_best=True)``. Requires ``eval_set``. When training - stops (or ends), the model is restored to the best iteration - and ``best_iteration_`` / ``best_score_`` are set. With - ``interactions > 0`` the main-effect and interaction rounds - form one monitored sequence; stopping during the main-effect - phase skips the interaction phase. - - Returns: - self: The fitted model. - """ - y = np.asarray(y, dtype=np.float32).ravel() - - # LOW-12: Initialize base score - loss_name = self.loss if isinstance(self.loss, str) else '' - if loss_name in ('logloss', 'binary_crossentropy'): - p = np.clip(np.mean(y), 1e-7, 1 - 1e-7) - self.base_score_ = np.float32(np.log(p / (1 - p))) - else: - self.base_score_ = np.float32(np.mean(y)) - - # Get loss function - self._loss_fn = get_loss_function(self.loss) - - # Bin the data - if isinstance(X, BinnedArray): - self.X_binned_ = X - else: - self.X_binned_ = array(X, n_bins=self.n_bins) - - # Reset per-fit state - self.pair_shape_values_ = {} - self.interaction_pairs_ = [] - self.evals_result_ = {} - - self._validate_monotone_features() - - cb_list = list(callbacks) if callbacks else [] - if early_stopping_rounds is not None: - cb_list.append( - EarlyStopping(patience=early_stopping_rounds, restore_best=True) - ) - eval_set = validate_eval_set(eval_set, self.X_binned_.n_features) - warn_if_early_stopping_without_eval_set(cb_list, eval_set) - - # Smoothing, monotone projection, and per-round callback/eval work all - # need host-side histograms, so they require the CPU loop. - needs_cpu_loop = ( - bool(cb_list) - or bool(eval_set) - or self.smoothing > 0 - or bool(self.monotone) - ) - - # Choose training path - if is_cuda() and not needs_cpu_loop: - self._fit_gpu(y) - if self.interactions > 0: - # Interaction boosting is CPU-only (documented); the GPU - # main-effects path is kept and the 2D stage runs on host. - data = self.X_binned_.data - binned = ( - data.copy_to_host() - if hasattr(data, 'copy_to_host') - else np.asarray(data) - ) - pred = self._predict_from_shape(binned, self.shape_values_) - n_inter = ( - self.n_rounds - if self.interaction_rounds is None - else self.interaction_rounds - ) - cb_manager = CallbackManager([]) - state = TrainingState(model=self, n_rounds=n_inter) - metric = self.loss if isinstance(self.loss, str) else 'loss' - self._boost_interactions( - binned, y, pred, cb_manager, state, [], metric, n_inter, 0 - ) - else: - if is_cuda(): - warnings.warn( - "OpenBoostGAM: smoothing/monotone/callbacks/eval_set " - "require the CPU training path; falling back to CPU " - "training (the GPU path is only used for plain fits).", - UserWarning, - stacklevel=2, - ) - self._fit_cpu(y, cb_list, eval_set) - - return self - - def _validate_monotone_features(self) -> None: - """Validate monotone feature indices against the binned data.""" - if not self.monotone: - return - n_features = self.X_binned_.n_features - is_cat = self.X_binned_.is_categorical - for f_idx in self.monotone: - if not 0 <= f_idx < n_features: - raise ValueError( - f"monotone feature index {f_idx} is out of range for " - f"data with {n_features} features" - ) - if len(is_cat) > f_idx and is_cat[f_idx]: - raise ValueError( - f"monotone constraint on feature {f_idx} is invalid: " - "the feature is categorical (bins are unordered)" - ) - - def _fit_gpu(self, y: NDArray): - """GPU training path - all features in parallel.""" - from numba import cuda - - from .._backends._cuda import build_histogram_cuda - - n_features = self.X_binned_.n_features - n_samples = self.X_binned_.n_samples - binned_gpu = self.X_binned_.data # (n_features, n_samples) on GPU - - # Initialize shape functions to zero. - # Always 256-wide regardless of n_bins: histogram kernels emit - # (n_features, 256) and uint8 bin indices (incl. MISSING_BIN=255) must - # never index out of bounds. - shape_values_gpu = cuda.device_array((n_features, 256), dtype=np.float32) - _fill_zeros_2d_gpu(shape_values_gpu) - - # Initialize predictions with base score - base = getattr(self, 'base_score_', np.float32(0.0)) - pred_host = np.full(n_samples, base, dtype=np.float32) - pred_gpu = cuda.to_device(pred_host) - - y_gpu = cuda.to_device(y) - - # Boosting loop - for _ in range(self.n_rounds): - # Compute gradients on GPU - grad_gpu, hess_gpu = self._loss_fn(pred_gpu, y_gpu) - - # Build histograms for ALL features (your existing kernel!) - hist_grad, hist_hess = build_histogram_cuda(binned_gpu, grad_gpu, hess_gpu) - - # Update ALL shape functions in parallel - threads = 256 - blocks = (n_features * 256 + threads - 1) // threads - _update_shape_functions_kernel[blocks, threads]( - hist_grad, hist_hess, shape_values_gpu, - np.float32(self.learning_rate), np.float32(self.reg_lambda) - ) - - # Update predictions using shape functions - threads = 256 - blocks = (n_samples + threads - 1) // threads - _predict_gam_kernel[blocks, threads](binned_gpu, shape_values_gpu, pred_gpu) - - self.shape_values_ = shape_values_gpu.copy_to_host() - - def _fit_cpu( - self, - y: NDArray, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ): - """CPU training path: main effects, then optional 2D interactions.""" - from .._backends._cpu import build_histogram_cpu - - n_features = self.X_binned_.n_features - n_samples = self.X_binned_.n_samples - - # Get binned data on CPU - if hasattr(self.X_binned_.data, 'copy_to_host'): - binned = self.X_binned_.data.copy_to_host() - else: - binned = np.asarray(self.X_binned_.data) - - # Always 256-wide regardless of n_bins: build_histogram_cpu returns - # (n_features, 256) and uint8 bin indices (incl. MISSING_BIN=255) must - # never index out of bounds. - self.shape_values_ = np.zeros((n_features, 256), dtype=np.float32) - shape_values = self.shape_values_ # live alias, mutated in place - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples, base, dtype=np.float32) - - n_inter_rounds = 0 - if self.interactions > 0: - n_inter_rounds = ( - self.n_rounds - if self.interaction_rounds is None - else self.interaction_rounds - ) - - metric = self.loss if isinstance(self.loss, str) else 'loss' - - # Bin eval sets once with the training bin edges - eval_data: list[tuple[NDArray, NDArray]] = [] - if eval_set: - for X_e, y_e in eval_set: - X_e_binned = ( - X_e if isinstance(X_e, BinnedArray) - else self.X_binned_.transform(X_e) - ) - data_e = X_e_binned.data - if hasattr(data_e, 'copy_to_host'): - data_e = data_e.copy_to_host() - eval_data.append( - (np.asarray(data_e), np.asarray(y_e, dtype=np.float32).ravel()) - ) - self.evals_result_ = { - f'eval_{i}': {metric: []} for i in range(len(eval_data)) - } - - # Per-bin sample counts anchor the monotone projection weights - bin_counts = None - if self.monotone: - bin_counts = np.zeros((n_features, 256), dtype=np.int64) - for f in range(n_features): - bin_counts[f] = np.bincount(binned[f], minlength=256) - - cb_manager = CallbackManager(callbacks) - state = TrainingState(model=self, n_rounds=self.n_rounds + n_inter_rounds) - cb_manager.on_train_begin(state) - - stopped = False - for round_idx in range(self.n_rounds): - state.round_idx = round_idx - cb_manager.on_round_begin(state) - - # Compute gradients - grad, hess = self._loss_fn(pred, y) - grad = grad.astype(np.float32) - hess = hess.astype(np.float32) - - # Build histograms - hist_grad, hist_hess = build_histogram_cpu(binned, grad, hess) - - # Update shape functions - mask = hist_hess > 0 - updates = np.zeros_like(hist_grad) - updates[mask] = -hist_grad[mask] / (hist_hess[mask] + self.reg_lambda) - - if self.smoothing > 0: - updates = self._smooth_updates(updates, hist_hess) - - shape_values += self.learning_rate * updates - - # Project onto the monotone cone AFTER each round (not once at - # fit end): subsequent rounds then compute gradients from the - # constrained model and boosting self-corrects for the - # projection. Projecting only at fit end would train against - # unconstrained predictions, so the final projection could - # arbitrarily distort the fit and the interaction stage would - # select pairs from residuals of a model that no longer exists. - if self.monotone: - self._apply_monotone(bin_counts) - - # Update predictions - pred = self._predict_from_shape(binned, shape_values) - - if not self._record_round(cb_manager, state, pred, y, eval_data, metric): - stopped = True - break - - if not stopped and self.interactions > 0: - self._boost_interactions( - binned, y, pred, cb_manager, state, eval_data, metric, - n_inter_rounds, self.n_rounds, - ) - - cb_manager.on_train_end(state) - - def _record_round( - self, - cb_manager: CallbackManager, - state: TrainingState, - pred: NDArray, - y: NDArray, - eval_data: list[tuple[NDArray, NDArray]], - metric: str, - ) -> bool: - """Record eval history and run round-end callbacks. - - Returns: - True to continue training, False to stop early. - """ - last_val = None - for i, (binned_e, y_e) in enumerate(eval_data): - pred_e = self._predict_host(binned_e) - val = compute_loss_value(self.loss, pred_e, y_e) - self.evals_result_[f'eval_{i}'][metric].append(val) - last_val = val - - if cb_manager.callbacks: - state.train_loss = compute_loss_value(self.loss, pred, y) - if last_val is not None: - # Early stopping monitors the LAST eval set's metric - state.val_loss = last_val - return cb_manager.on_round_end(state) - return True - - def _smooth_updates(self, updates: NDArray, hist_hess: NDArray) -> NDArray: - """Fused-ridge smoothing of one round's per-feature Newton updates. - - Solves ``(W + smoothing * D^T D) s = W u`` per feature, where ``D`` is - the first-difference matrix over the feature's ordinal bins and ``W`` - is the 0/1 occupancy diagonal (``hist_hess > 0``). Occupied bins - anchor the solution at the raw Newton update; empty bins carry no data - term and interpolate between their neighbors. The system is symmetric - tridiagonal and positive definite whenever at least one bin is - occupied, so it is solved with a banded Cholesky solve. - - Only ordinal bins ``0..len(bin_edges)-1`` participate: categorical - features (no numeric edges) and the missing-value bin (255) keep - their raw updates, since adjacency is meaningless for them. - """ - from scipy.linalg import solveh_banded - - s = float(self.smoothing) - smoothed = updates.copy() - edges_list = self.X_binned_.bin_edges - for f in range(updates.shape[0]): - edges = edges_list[f] if f < len(edges_list) else () - n_used = len(edges) - if n_used < 2: - continue # constant or categorical feature: nothing to smooth - w = (hist_hess[f, :n_used] > 0).astype(np.float64) - if not np.any(w): - continue - u = updates[f, :n_used].astype(np.float64) - # D^T D is tridiagonal: diag [1, 2, ..., 2, 1], off-diag -1 - diag = np.full(n_used, 2.0) - diag[0] = diag[-1] = 1.0 - ab = np.zeros((2, n_used)) - ab[0, 1:] = -s - ab[1, :] = w + s * diag - smoothed[f, :n_used] = solveh_banded(ab, w * u, lower=False).astype( - np.float32 - ) - return smoothed - - def _apply_monotone(self, bin_counts: NDArray) -> None: - """Project constrained 1D shape functions onto the monotone cone. - - Weighted isotonic regression (PAVA) over the feature's ordinal bins, - weighted by per-bin sample counts so empty bins (which may still be - hit by test data) conform to the constraint without influencing the - anchored values. The missing-value bin (255) is unconstrained. - """ - edges_list = self.X_binned_.bin_edges - for f_idx, direction in self.monotone.items(): - edges = edges_list[f_idx] if f_idx < len(edges_list) else () - n_used = len(edges) - if n_used < 2: - continue - w = np.maximum(bin_counts[f_idx, :n_used].astype(np.float64), 1e-9) - self.shape_values_[f_idx, :n_used] = _pava( - self.shape_values_[f_idx, :n_used], w, increasing=direction > 0 - ) - - def _select_interaction_pairs( - self, binned: NDArray, grad: NDArray, hess: NDArray - ) -> list[tuple[int, int]]: - """Rank feature pairs FAST-style on main-effects residuals. - - For every pair (i, j) the potential is the Newton gain of a one-shot - 2D histogram step, ``sum_cells g^2 / (h + reg_lambda)``, minus both - features' 1D gains on the same residuals — i.e. the extra structure a - 2D grid captures beyond what either 1D refit could. Rows are - subsampled (deterministically) for ranking on large datasets. - """ - from .._backends._cpu import build_histogram_cpu - - n_features, n_samples = binned.shape - max_rank_rows = 50_000 - if n_samples > max_rank_rows: - idx = np.random.default_rng(0).choice( - n_samples, max_rank_rows, replace=False - ) - idx.sort() - b = np.ascontiguousarray(binned[:, idx]) - g, h = grad[idx], hess[idx] - else: - b, g, h = binned, grad, hess - - lam = float(self.reg_lambda) - hist_g, hist_h = build_histogram_cpu(b, g, h) - hist_g = hist_g.astype(np.float64) - hist_h = hist_h.astype(np.float64) - gain1 = np.where(hist_h > 0, hist_g**2 / (hist_h + lam), 0.0).sum(axis=1) - - g64 = g.astype(np.float64) - h64 = h.astype(np.float64) - scored: list[tuple[float, int, int]] = [] - for i in range(n_features): - row_i = b[i].astype(np.intp) * 256 - for j in range(i + 1, n_features): - combined = row_i + b[j] - g2 = np.bincount(combined, weights=g64, minlength=65536) - h2 = np.bincount(combined, weights=h64, minlength=65536) - m = h2 > 0 - gain2 = float((g2[m] ** 2 / (h2[m] + lam)).sum()) - scored.append((gain2 - float(gain1[i]) - float(gain1[j]), i, j)) - - # Deterministic ranking: score desc, then (i, j) asc as tie-break - scored.sort(key=lambda t: (-t[0], t[1], t[2])) - k = min(self.interactions, len(scored)) - return [(i, j) for _, i, j in scored[:k]] - - def _boost_interactions( - self, - binned: NDArray, - y: NDArray, - pred: NDArray, - cb_manager: CallbackManager, - state: TrainingState, - eval_data: list[tuple[NDArray, NDArray]], - metric: str, - n_inter_rounds: int, - round_offset: int, - ) -> None: - """Select top-k feature pairs and boost their 2D shape tables (CPU).""" - # Residual gradients of the fitted main-effects model drive ranking - grad, hess = self._loss_fn(pred, y) - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - - pairs = self._select_interaction_pairs(binned, grad, hess) - self.interaction_pairs_ = pairs - if not pairs or n_inter_rounds == 0: - return - - # Precompute flattened (bin_i * 256 + bin_j) indices per pair - combined_idx: dict[tuple[int, int], NDArray] = {} - for i, j in pairs: - combined_idx[(i, j)] = binned[i].astype(np.intp) * 256 + binned[j] - self.pair_shape_values_[(i, j)] = np.zeros((256, 256), dtype=np.float32) - - lam = float(self.reg_lambda) - for r in range(n_inter_rounds): - state.round_idx = round_offset + r - cb_manager.on_round_begin(state) - - grad, hess = self._loss_fn(pred, y) - g64 = np.asarray(grad, dtype=np.float64) - h64 = np.asarray(hess, dtype=np.float64) - - # All selected pairs take one parallel Newton step per round, - # matching the parallel per-feature updates of the main loop. - for pair in pairs: - comb = combined_idx[pair] - g2 = np.bincount(comb, weights=g64, minlength=65536) - h2 = np.bincount(comb, weights=h64, minlength=65536) - upd = np.zeros(65536, dtype=np.float32) - m = h2 > 0 - upd[m] = (-g2[m] / (h2[m] + lam)).astype(np.float32) - self.pair_shape_values_[pair] += ( - self.learning_rate * upd.reshape(256, 256) - ) - # Incremental prediction update is exact: this round only - # adds `lr * upd` to this pair's lookup table. - pred += self.learning_rate * upd[comb] - - if not self._record_round(cb_manager, state, pred, y, eval_data, metric): - break - - def _predict_from_shape(self, binned: NDArray, shape_values: NDArray) -> NDArray: - """CPU prediction using 1D shape functions only.""" - n_samples = binned.shape[1] - n_features = binned.shape[0] - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples, base, dtype=np.float32) - for f in range(n_features): - pred += shape_values[f, binned[f, :]] - return pred - - def _predict_host(self, binned: NDArray) -> NDArray: - """Full CPU prediction: base + 1D shape lookups + 2D pair lookups.""" - pred = self._predict_from_shape(binned, self.shape_values_) - pairs = getattr(self, 'pair_shape_values_', None) - if pairs: - for (i, j), table in pairs.items(): - pred += table[binned[i, :], binned[j, :]] - return pred - - def predict(self, X: NDArray | BinnedArray) -> NDArray: - """Generate predictions. - - Args: - X: Features, shape (n_samples, n_features). - - Returns: - predictions: Shape (n_samples,). - """ - if self.shape_values_ is None: - raise RuntimeError("Model not fitted. Call fit() first.") - - # Bin the data if needed, using training bin edges for consistency - if isinstance(X, BinnedArray): - X_binned = X - elif self.X_binned_ is not None: - X_binned = self.X_binned_.transform(X) - else: - X_binned = array(X, n_bins=self.n_bins) - - n_samples = X_binned.n_samples - - # The GPU kernel only handles 1D lookups; models with interaction - # tables predict on host (documented CPU-only interaction support). - if is_cuda() and not getattr(self, 'pair_shape_values_', None): - from numba import cuda - - binned_gpu = X_binned.data - shape_gpu = cuda.to_device(self.shape_values_) - pred_gpu = cuda.device_array(n_samples, dtype=np.float32) - - threads = 256 - blocks = (n_samples + threads - 1) // threads - _predict_gam_kernel[blocks, threads](binned_gpu, shape_gpu, pred_gpu) - - result = pred_gpu.copy_to_host() - base = getattr(self, 'base_score_', np.float32(0.0)) - if base != 0.0: - result += base - return result - else: - if hasattr(X_binned.data, 'copy_to_host'): - binned = X_binned.data.copy_to_host() - else: - binned = np.asarray(X_binned.data) - return self._predict_host(binned) - - def predict_proba(self, X: NDArray | BinnedArray) -> NDArray: - """Predict class probabilities (for classification). - - Only valid when loss='logloss'. - """ - if self.loss not in ('logloss', 'binary_crossentropy'): - raise ValueError("predict_proba only available for classification losses") - - raw_pred = self.predict(X) - prob_1 = 1 / (1 + np.exp(-raw_pred)) - prob_0 = 1 - prob_1 - return np.column_stack([prob_0, prob_1]) - - def get_feature_importance(self) -> NDArray: - """Get feature importance based on shape function variance. - - Returns: - importance: Shape (n_features,), higher = more important. - """ - if self.shape_values_ is None: - raise RuntimeError("Model not fitted.") - - # Importance = variance of shape function (how much it varies) - return np.var(self.shape_values_, axis=1) - - def get_shape_function(self, feature_idx: int) -> NDArray: - """Get the shape function values for a feature. - - Args: - feature_idx: Index of the feature. - - Returns: - values: Shape (256,), contribution for each bin. - """ - if self.shape_values_ is None: - raise RuntimeError("Model not fitted.") - return self.shape_values_[feature_idx].copy() - - def get_pair_shape_function(self, i: int, j: int) -> NDArray: - """Get the 2D interaction shape table for feature pair (i, j). - - Args: - i: First feature index (order-insensitive). - j: Second feature index. - - Returns: - values: Shape (256, 256), contribution for each (bin_i, bin_j). - - Raises: - RuntimeError: If the model is not fitted. - KeyError: If the pair was not selected during fitting. - """ - if self.shape_values_ is None: - raise RuntimeError("Model not fitted.") - pairs = getattr(self, 'pair_shape_values_', {}) or {} - key = (i, j) if (i, j) in pairs else (j, i) - if key not in pairs: - raise KeyError( - f"No interaction table for feature pair ({i}, {j}). " - f"Selected pairs: {sorted(pairs)}" - ) - return pairs[key].copy() - - def plot_shape_function(self, feature_idx: int, feature_name: str | None = None, ax=None): - """Plot the shape function for a feature. - - The x-axis shows original feature values, recovered from the bin - edges learned at fit time. Categorical or constant features (which - have no numeric bin edges) fall back to raw bin indices. The - contribution learned for the missing-value bin (255) is not shown. - - Args: - feature_idx: Index of the feature to plot. - feature_name: Optional name for the x-axis label. - ax: Optional matplotlib Axes to draw on. When None, a new - figure and axes are created. - - Returns: - The matplotlib Axes containing the plot. - - Raises: - RuntimeError: If the model is not fitted. - ValueError: If feature_idx is out of range. - """ - if self.shape_values_ is None: - raise RuntimeError("Model not fitted. Call fit() first.") - - n_features = self.shape_values_.shape[0] - if not 0 <= feature_idx < n_features: - raise ValueError( - f"feature_idx={feature_idx} is out of range for a model " - f"fitted with {n_features} features" - ) - - try: - import matplotlib.pyplot as plt - except ImportError as err: - raise ImportError("matplotlib required for plotting. Install with: pip install matplotlib") from err - - values = self.shape_values_[feature_idx] - label = feature_name if feature_name is not None else f"Feature {feature_idx}" - - edges = np.array([], dtype=np.float64) - if self.X_binned_ is not None and feature_idx < len(self.X_binned_.bin_edges): - edges = np.asarray(self.X_binned_.bin_edges[feature_idx], dtype=np.float64) - - if edges.size > 0: - # ob.array bins via searchsorted + clip, so occupied bins are - # 0..len(edges)-1: bin 0 lies below edges[0], bin b between - # edges[b-1] and edges[b], and the last bin extends past - # edges[-1]. Use edge midpoints as representative x positions, - # anchored at the outermost edges. - n_used = edges.size - x = np.empty(n_used, dtype=np.float64) - x[0] = edges[0] - x[-1] = edges[-1] - if n_used > 2: - x[1:-1] = 0.5 * (edges[:-2] + edges[1:-1]) - y_vals = values[:n_used] - xlabel = label - else: - x = np.arange(values.shape[0]) - y_vals = values - xlabel = f"{label} (binned value)" - - created_fig = ax is None - if created_fig: - fig, ax = plt.subplots(figsize=(10, 4)) - - ax.step(x, y_vals, where='mid') - ax.axhline(y=0, color='k', linestyle='-', linewidth=0.5) - ax.set_xlabel(xlabel) - ax.set_ylabel("Contribution to prediction") - ax.set_title(f"Shape Function: {label}") - if created_fig: - fig.tight_layout() - return ax - - -# ============================================================================= -# Helpers -# ============================================================================= - - -def _pava(values: NDArray, weights: NDArray, increasing: bool = True) -> NDArray: - """Weighted isotonic regression via pool-adjacent-violators (PAVA). - - Returns the monotone (non-decreasing when ``increasing``, else - non-increasing) sequence minimizing the weighted squared error to - ``values``. Weights must be strictly positive. - """ - v = np.asarray(values, dtype=np.float64) - if not increasing: - v = -v - w = np.asarray(weights, dtype=np.float64) - - means: list[float] = [] - wsums: list[float] = [] - counts: list[int] = [] - for k in range(v.shape[0]): - m, ww, c = float(v[k]), float(w[k]), 1 - # Pool with the previous block while it violates monotonicity - while means and means[-1] > m: - m_prev, w_prev, c_prev = means.pop(), wsums.pop(), counts.pop() - total = w_prev + ww - m = (m_prev * w_prev + m * ww) / total - ww = total - c += c_prev - means.append(m) - wsums.append(ww) - counts.append(c) - - out = np.repeat(np.asarray(means), np.asarray(counts)) - return out if increasing else -out - - -# ============================================================================= -# CUDA Kernels -# ============================================================================= - -_update_shape_functions_kernel = None -_predict_gam_kernel = None -_fill_zeros_2d_kernel = None -_fill_zeros_1d_kernel = None - - -def _init_cuda_kernels(): - """Initialize CUDA kernels lazily.""" - global _update_shape_functions_kernel, _predict_gam_kernel - global _fill_zeros_2d_kernel, _fill_zeros_1d_kernel - - if _update_shape_functions_kernel is not None: - return - - from numba import cuda, float32, int32 - - @cuda.jit - def update_shape_kernel(hist_grad, hist_hess, shape_values, learning_rate, reg_lambda): - """Update all shape functions in parallel from histograms.""" - idx = cuda.grid(1) - n_features = hist_grad.shape[0] - total_elements = n_features * 256 - - if idx < total_elements: - feature = idx // 256 - bin_idx = idx % 256 - - g = hist_grad[feature, bin_idx] - h = hist_hess[feature, bin_idx] - - if h > float32(0.0): - update = -g / (h + reg_lambda) - shape_values[feature, bin_idx] += learning_rate * update - - @cuda.jit - def predict_kernel(binned, shape_values, predictions): - """Predict by summing shape function lookups.""" - sample_idx = cuda.grid(1) - n_features = binned.shape[0] - n_samples = binned.shape[1] - - if sample_idx < n_samples: - total = float32(0.0) - for f in range(n_features): - bin_idx = binned[f, sample_idx] - total += shape_values[f, int32(bin_idx)] - predictions[sample_idx] = total - - @cuda.jit - def fill_zeros_2d(arr): - """Fill 2D array with zeros.""" - idx = cuda.grid(1) - rows, cols = arr.shape - total = rows * cols - if idx < total: - r = idx // cols - c = idx % cols - arr[r, c] = float32(0.0) - - @cuda.jit - def fill_zeros_1d(arr): - """Fill 1D array with zeros.""" - idx = cuda.grid(1) - if idx < arr.shape[0]: - arr[idx] = float32(0.0) - - _update_shape_functions_kernel = update_shape_kernel - _predict_gam_kernel = predict_kernel - _fill_zeros_2d_kernel = fill_zeros_2d - _fill_zeros_1d_kernel = fill_zeros_1d - - -def _fill_zeros_2d_gpu(arr): - """Fill 2D GPU array with zeros.""" - _init_cuda_kernels() - rows, cols = arr.shape - total = rows * cols - threads = 256 - blocks = (total + threads - 1) // threads - _fill_zeros_2d_kernel[blocks, threads](arr) - - -def _fill_zeros_1d_gpu(arr): - """Fill 1D GPU array with zeros.""" - _init_cuda_kernels() - n = arr.shape[0] - threads = 256 - blocks = (n + threads - 1) // threads - _fill_zeros_1d_kernel[blocks, threads](arr) - - -# Initialize kernels if CUDA available -if is_cuda(): - try: - _init_cuda_kernels() - except Exception: - warnings.warn("Failed to compile GAM CUDA kernels; will retry on first use", stacklevel=1) diff --git a/src/openboost/_models/_linear_leaf.py b/src/openboost/_models/_linear_leaf.py deleted file mode 100644 index 03c77b2..0000000 --- a/src/openboost/_models/_linear_leaf.py +++ /dev/null @@ -1,609 +0,0 @@ -"""Linear Leaf Gradient Boosting. - -Phase 15.4: Trees with linear models in leaves for better extrapolation. - -Each leaf fits: y = w0 + w1*x1 + w2*x2 + ... -instead of a constant value. - -Benefits: -- Better extrapolation beyond training data range -- Smoother predictions at decision boundaries -- Can use shallower trees (linear models add flexibility) -- Better performance on data with linear trends - -Reference: - Similar to LightGBM's linear tree feature. - -Example: - ```python - import openboost as ob - - model = ob.LinearLeafGBDT(n_trees=100, max_depth=4) - model.fit(X_train, y_train) - pred = model.predict(X_test) # Better extrapolation! - ``` -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import TYPE_CHECKING - -import numpy as np - -from .._array import BinnedArray, array -from .._callbacks import ( - Callback, - CallbackManager, - EarlyStopping, - TrainingState, - warn_if_early_stopping_without_eval_set, -) -from .._core._growth import TreeStructure -from .._core._tree import fit_tree -from .._loss import LossFunction, get_loss_function -from .._persistence import PersistenceMixin -from .._validation import validate_eval_set - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -def _binned_matrix(x_binned) -> NDArray: - """Extract a host (n_features, n_samples) uint8 matrix from binned data.""" - data = x_binned.data if isinstance(x_binned, BinnedArray) else x_binned - if hasattr(data, 'copy_to_host'): - data = data.copy_to_host() - return np.asarray(data) - - -def _route_to_leaves(tree: TreeStructure, binned: NDArray) -> NDArray: - """Route all samples to their leaf node index, vectorized. - - Descends every sample in lockstep: each iteration advances all samples - still at internal nodes one level down, so the loop runs over tree depth, - never over samples. - - Args: - tree: TreeStructure with left/right children routing arrays - binned: Binned features, shape (n_features, n_samples) - - Returns: - node_ids: (n_samples,) int32 tree node index of each sample's leaf - """ - left = np.asarray(tree.left_children) - right = np.asarray(tree.right_children) - features = np.asarray(tree.features) - thresholds = np.asarray(tree.thresholds) - - n_samples = binned.shape[1] - node = np.zeros(n_samples, dtype=np.int32) - active = np.nonzero(left[node] != -1)[0] - while active.size: - idx = node[active] - go_left = binned[features[idx], active] <= thresholds[idx] - node[active] = np.where(go_left, left[idx], right[idx]) - active = active[left[node[active]] != -1] - return node - - -@dataclass -class LinearLeafTree: - """A tree with linear models in leaves. - - Instead of constant leaf values, each leaf has a linear model: - prediction = w0 + w1*x[f1] + w2*x[f2] + ... - - Attributes: - tree_structure: Base tree for routing samples to leaves - leaf_weights: (n_leaves, max_features + 1) linear model weights - leaf_features: List of feature indices used in each leaf - leaf_ids: Mapping from integer tree node index to our leaf index - n_features: Total number of features in the dataset - training_binned: Reference to training BinnedArray for transform - """ - tree_structure: TreeStructure - leaf_weights: NDArray # (n_leaves, max_features_linear + 1) - leaf_features: list[list[int]] # Features used per leaf - leaf_ids: dict[int, int] # Map tree node index -> our leaf index - n_features: int = 0 - training_binned: BinnedArray | None = None # For transform - - def __call__(self, X: NDArray) -> NDArray: - """Predict using linear leaf tree.""" - return self.predict(X) - - def predict(self, X: NDArray, binned: NDArray | None = None) -> NDArray: - """Generate predictions using linear models in leaves. - - Args: - X: Features, shape (n_samples, n_features) - binned: Optional precomputed binned matrix (n_features, n_samples) - from this tree's training bin edges; avoids re-binning when - predicting through many trees that share bin edges. - - Returns: - predictions: Shape (n_samples,) - """ - X = np.asarray(X, dtype=np.float32) - n_samples = X.shape[0] - - # Get leaf node indices from tree structure - leaf_node_ids = self._get_leaf_node_indices(X, binned=binned) - - # Map tree node index -> leaf index. Unknown node ids fall back to - # leaf 0 (same behavior as the original per-sample lookup). - node_to_leaf = np.zeros(self.tree_structure.n_nodes, dtype=np.int32) - for node_id, leaf_idx in self.leaf_ids.items(): - node_to_leaf[node_id] = leaf_idx - sample_leaf = node_to_leaf[leaf_node_ids] - - predictions = np.zeros(n_samples, dtype=np.float32) - - for leaf_idx, feat_indices in enumerate(self.leaf_features): - rows = np.nonzero(sample_leaf == leaf_idx)[0] - if rows.size == 0: - continue - - weights = self.leaf_weights[leaf_idx] - - # Linear prediction: w0 + sum(w_i * x_i), batched over the leaf's - # samples. Accumulate feature-by-feature in float32 to stay - # bit-identical to the original scalar accumulation order. - leaf_pred = np.full(rows.size, weights[0], dtype=np.float32) - n_terms = min(len(feat_indices), len(weights) - 1) - for j in range(n_terms): - leaf_pred += weights[j + 1] * X[rows, feat_indices[j]] - - predictions[rows] = leaf_pred - - return predictions - - def _get_leaf_node_indices(self, X: NDArray, binned: NDArray | None = None) -> NDArray: - """Get the tree node index of the leaf each sample falls into.""" - if binned is None: - # Bin the data for tree prediction, using training bin edges - if self.training_binned is not None: - X_binned = self.training_binned.transform(X) - else: - X_binned = array(X) - binned = _binned_matrix(X_binned) - - return _route_to_leaves(self.tree_structure, binned) - - -@dataclass -class LinearLeafGBDT(PersistenceMixin): - """Gradient Boosting with Linear Leaf Trees. - - Each tree has linear models in its leaves instead of constant values. - This enables: - - Better extrapolation beyond training data range - - Smoother decision boundaries - - Can use shallower trees (linear models add complexity) - - Recommended settings: - - Use max_depth=3-4 (shallower than standard GBDT) - - Use larger min_samples_leaf (need samples to fit linear model) - - Args: - n_trees: Number of boosting rounds - max_depth: Maximum tree depth (typically 3-4, shallower than standard) - learning_rate: Shrinkage factor - loss: Loss function ('mse', 'mae', 'huber', or callable) - min_samples_leaf: Minimum samples to fit linear model in leaf - reg_lambda_tree: L2 regularization for tree splits - reg_lambda_linear: L2 regularization for linear models (ridge) - max_features_linear: Max features per leaf's linear model - - None: Use all features - - 'sqrt': Use sqrt(n_features) features - - 'log2': Use log2(n_features) features - - int: Use exactly this many features - n_bins: Number of bins for histogram building - - Attributes (after fit with observability args): - evals_result_: Per-round eval-set MSE history recorded during - training, e.g. ``{'eval_0': {'mse': [...]}}``. Empty dict when - no ``eval_set`` was passed to ``fit()``. - best_iteration_: Best round index (set when early stopping is used). - best_score_: Best monitored metric value (set with best_iteration_). - - - Example: - ```python - model = LinearLeafGBDT(n_trees=100, max_depth=4) - model.fit(X_train, y_train) - pred = model.predict(X_test) - - # Compare extrapolation with standard GBDT - from openboost import GradientBoosting - standard = GradientBoosting(n_trees=100, max_depth=6) - standard.fit(X_train, y_train) - # LinearLeafGBDT typically extrapolates better on linear trends - ``` - """ - - n_trees: int = 100 - max_depth: int = 4 # Typically shallower than standard GBDT - learning_rate: float = 0.1 - loss: str | LossFunction = 'mse' - min_samples_leaf: int = 20 # Need enough samples for linear fit - reg_lambda_tree: float = 1.0 - reg_lambda_linear: float = 0.1 # Ridge regularization for linear models - max_features_linear: int | str | None = 'sqrt' - n_bins: int = 254 - - # Fitted attributes (not init) - trees_: list[LinearLeafTree] = field(default_factory=list, init=False, repr=False) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _loss_fn: LossFunction | None = field(default=None, init=False, repr=False) - n_features_in_: int = field(default=0, init=False, repr=False) - evals_result_: dict[str, dict[str, list[float]]] = field( - default_factory=dict, init=False, repr=False - ) - - def fit( - self, - X: NDArray, - y: NDArray, - callbacks: list[Callback] | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - early_stopping_rounds: int | None = None, - ) -> LinearLeafGBDT: - """Fit the linear leaf GBDT model. - - Args: - X: Training features, shape (n_samples, n_features) - y: Training targets, shape (n_samples,) - callbacks: List of Callback instances (e.g. EarlyStopping, - Logger) invoked each boosting round with a TrainingState - carrying the round index, train MSE, and val MSE. - eval_set: Validation set(s) as a list of ``(X_val, y_val)`` - tuples (a single bare tuple is also accepted). Every eval - set is scored with MSE each round and the per-round history - is stored in ``evals_result_``. The LAST eval set's MSE is - reported to callbacks as ``val_loss``. - early_stopping_rounds: Stop training when the monitored eval-set - MSE has not improved for this many consecutive rounds - (sugar for an ``EarlyStopping`` callback with - ``restore_best=True``). The model is restored to (truncated - at) the best iteration and ``best_iteration_`` / - ``best_score_`` are set. Requires ``eval_set``. - - Returns: - self: Fitted model - """ - self.trees_ = [] - - X = np.asarray(X, dtype=np.float32) - y = np.asarray(y, dtype=np.float32).ravel() - n_samples, n_features = X.shape - - self.n_features_in_ = n_features - - # Store raw X for linear fitting (need un-binned values) - self._X_raw = X - - # Get loss function - self._loss_fn = get_loss_function(self.loss) - - # Bin data for tree building - self.X_binned_ = array(X, n_bins=self.n_bins) - - # Determine max features for linear models - if self.max_features_linear is None: - n_linear_features = n_features - elif self.max_features_linear == 'sqrt': - n_linear_features = max(1, int(np.sqrt(n_features))) - elif self.max_features_linear == 'log2': - n_linear_features = max(1, int(np.log2(n_features))) - elif isinstance(self.max_features_linear, int): - n_linear_features = min(self.max_features_linear, n_features) - else: - n_linear_features = n_features - - self._n_linear_features = n_linear_features - - # Initialize predictions with base score - if self.loss in ('logloss', 'binary_crossentropy'): - p = np.clip(np.mean(y), 1e-7, 1 - 1e-7) - self.base_score_ = np.float32(np.log(p / (1 - p))) - else: - self.base_score_ = np.float32(np.mean(y)) - pred = np.full(n_samples, self.base_score_, dtype=np.float32) - - # Setup callbacks (early_stopping_rounds is sugar for EarlyStopping). - # EarlyStopping's restore_best snapshots/restores self.trees_; each - # LinearLeafTree bundles its routing tree AND its per-leaf linear - # models, so restoring trees_ truncates both consistently. - cb_list = list(callbacks) if callbacks else [] - if early_stopping_rounds is not None: - cb_list.append( - EarlyStopping(patience=early_stopping_rounds, restore_best=True) - ) - cb_manager = CallbackManager(cb_list) - state = TrainingState(model=self, n_rounds=self.n_trees) - - eval_set = validate_eval_set(eval_set, n_features) - warn_if_early_stopping_without_eval_set(cb_list, eval_set) - - # Per-eval-set state: raw features (linear leaves need un-binned - # values), targets, binned matrix (binned once with the training bin - # edges), and incrementally maintained predictions. - eval_data = [] - if eval_set: - for X_e, y_e in eval_set: - X_e = np.asarray(X_e, dtype=np.float32) - y_e = np.asarray(y_e, dtype=np.float32).ravel() - binned_e = _binned_matrix(self.X_binned_.transform(X_e)) - pred_e = np.full(X_e.shape[0], self.base_score_, dtype=np.float32) - eval_data.append((X_e, y_e, binned_e, pred_e)) - - self.evals_result_ = { - f'eval_{i}': {'mse': []} for i in range(len(eval_data)) - } - - cb_manager.on_train_begin(state) - - for round_idx in range(self.n_trees): - state.round_idx = round_idx - cb_manager.on_round_begin(state) - - # Compute gradients - grad, hess = self._loss_fn(pred, y) - grad = np.asarray(grad, dtype=np.float32) - hess = np.asarray(hess, dtype=np.float32) - - # Build tree structure (just for routing) - base_tree = fit_tree( - self.X_binned_, - grad, - hess, - max_depth=self.max_depth, - min_child_weight=float(self.min_samples_leaf), - reg_lambda=self.reg_lambda_tree, - ) - - # Fit linear models in each leaf - linear_tree = self._fit_linear_leaves( - base_tree, X, y, grad, hess, pred, n_linear_features - ) - - self.trees_.append(linear_tree) - - # Update predictions - tree_pred = linear_tree.predict(X) - pred = pred + self.learning_rate * tree_pred - - # Observability: only computed when requested, so the default - # path (no callbacks, no eval_set) is byte-identical to before. - if cb_manager.callbacks or eval_data: - # Train loss: MSE on current predictions (already tracked) - state.train_loss = float(np.mean((pred - y) ** 2)) - - # Score ALL eval sets, record history; callbacks monitor the - # LAST eval set's MSE (matches DistributionalGBDT semantics) - val_mse = None - for i, (X_e, y_e, binned_e, pred_e) in enumerate(eval_data): - pred_e += self.learning_rate * linear_tree.predict( - X_e, binned=binned_e - ) - val_mse = float(np.mean((pred_e - y_e) ** 2)) - self.evals_result_[f'eval_{i}']['mse'].append(val_mse) - if val_mse is not None: - state.val_loss = val_mse - - # Check if callbacks want to stop - if not cb_manager.on_round_end(state): - break - - cb_manager.on_train_end(state) - - # MED-21: Release raw data reference to free memory - self._X_raw = None - - return self - - def _fit_linear_leaves( - self, - base_tree: TreeStructure, - X: NDArray, - y: NDArray, - grad: NDArray, - hess: NDArray, - current_pred: NDArray, - n_linear_features: int, - ) -> LinearLeafTree: - """Fit linear models in each leaf of the tree. - - Uses weighted least squares with hessian as weights. - Target is the negative gradient divided by hessian (Newton step). - """ - n_samples, n_features = X.shape - - # Get leaf node indices (integer IDs) for each sample - binned_data = _binned_matrix(self.X_binned_) - leaf_node_ids = _route_to_leaves(base_tree, binned_data) - - # Find unique leaf node IDs - unique_node_ids = np.unique(leaf_node_ids) - n_leaves = len(unique_node_ids) - - # Map integer node index -> our leaf index - leaf_ids = {int(nid): i for i, nid in enumerate(unique_node_ids)} - - # Storage for linear models - leaf_weights = np.zeros((n_leaves, n_linear_features + 1), dtype=np.float32) - leaf_features = [] - - for leaf_idx, node_id in enumerate(unique_node_ids): - # Get samples in this leaf - mask = leaf_node_ids == node_id - n_leaf = np.sum(mask) - - if n_leaf < self.min_samples_leaf: - # Not enough samples: use constant (weighted mean of target) - w = hess[mask] - target = -grad[mask] / (hess[mask] + 1e-6) - if np.sum(w) > 0: - leaf_weights[leaf_idx, 0] = np.average(target, weights=w) - leaf_features.append([]) - continue - - # Get data for this leaf - X_leaf = X[mask] - grad_leaf = grad[mask] - hess_leaf = hess[mask] - - # Target for regression: Newton step = -grad/hess - target = -grad_leaf / (hess_leaf + 1e-6) - - # Select features based on correlation with target - if n_linear_features < n_features: - selected_features = self._select_features( - X_leaf, target, n_linear_features - ) - else: - selected_features = list(range(n_features)) - - leaf_features.append(selected_features) - - # Fit ridge regression with hessian weights - weights = self._fit_weighted_ridge( - X_leaf[:, selected_features], - target, - hess_leaf, - self.reg_lambda_linear, - ) - - # Store weights (bias first, then feature weights) - leaf_weights[leaf_idx, :len(weights)] = weights - - return LinearLeafTree( - tree_structure=base_tree, - leaf_weights=leaf_weights, - leaf_features=leaf_features, - leaf_ids=leaf_ids, - n_features=n_features, - training_binned=self.X_binned_, # For transform on new data - ) - - def _select_features( - self, - X: NDArray, - target: NDArray, - n_select: int, - ) -> list[int]: - """Select features based on correlation with target. - - Uses absolute correlation to select most relevant features. - """ - n_features = X.shape[1] - - correlations = np.zeros(n_features) - for j in range(n_features): - if np.std(X[:, j]) > 1e-8: - corr = np.corrcoef(X[:, j], target)[0, 1] - if not np.isnan(corr): - correlations[j] = abs(corr) - - # Select top features by correlation - selected = np.argsort(correlations)[-n_select:] - return sorted(selected.tolist()) - - def _fit_weighted_ridge( - self, - X: NDArray, - y: NDArray, - weights: NDArray, - reg_lambda: float, - ) -> NDArray: - """Fit weighted ridge regression. - - Minimizes: sum(w_i * (y_i - X_i @ beta)^2) + lambda * ||beta[1:]||^2 - - Solution: (X'WX + lambda*I)^{-1} X'Wy - (Don't regularize the bias term) - """ - n_samples, n_features = X.shape - - # Add bias column - X_aug = np.column_stack([np.ones(n_samples), X]) - - # Regularization (don't regularize bias) - reg_matrix = reg_lambda * np.eye(n_features + 1) - reg_matrix[0, 0] = 0 - - try: - # Solve normal equations — O(n*p^2) instead of O(n^2) via np.diag - XtWX = X_aug.T @ (weights[:, None] * X_aug) + reg_matrix - XtWy = X_aug.T @ (weights * y) - beta = np.linalg.solve(XtWX, XtWy) - except np.linalg.LinAlgError: - # Fallback: just use weighted mean - beta = np.zeros(n_features + 1) - beta[0] = np.average(y, weights=weights) if np.sum(weights) > 0 else 0 - - return beta.astype(np.float32) - - def predict(self, X: NDArray) -> NDArray: - """Generate predictions. - - Args: - X: Features, shape (n_samples, n_features) - - Returns: - predictions: Shape (n_samples,) - """ - if not self.trees_: - raise RuntimeError("Model not fitted. Call fit() first.") - - X = np.asarray(X, dtype=np.float32) - n_samples = X.shape[0] - - # All trees share the training bin edges, so bin X once instead of - # once per tree. - shared_binned = None - first_binned = self.trees_[0].training_binned - if first_binned is not None and all( - t.training_binned is first_binned for t in self.trees_ - ): - shared_binned = _binned_matrix(first_binned.transform(X)) - - base = getattr(self, 'base_score_', np.float32(0.0)) - pred = np.full(n_samples, base, dtype=np.float32) - for tree in self.trees_: - pred = pred + self.learning_rate * tree.predict(X, binned=shared_binned) - - return pred - - def score(self, X: NDArray, y: NDArray) -> float: - """R² score (coefficient of determination). - - Args: - X: Features - y: True target values - - Returns: - R² score (1.0 is perfect, 0.0 is baseline) - """ - y = np.asarray(y, dtype=np.float32) - y_pred = self.predict(X) - - ss_res = np.sum((y - y_pred) ** 2) - ss_tot = np.sum((y - np.mean(y)) ** 2) - - if ss_tot == 0: - return 0.0 - - return 1.0 - ss_res / ss_tot - - def _post_load(self) -> None: - """Post-load hook to restore tree references. - - After model is loaded, update all LinearLeafTree instances - with the restored X_binned_ reference for correct transform behavior. - """ - if hasattr(self, 'X_binned_') and self.X_binned_ is not None: - for tree in self.trees_: - tree.training_binned = self.X_binned_ diff --git a/src/openboost/_models/_sklearn.py b/src/openboost/_models/_sklearn.py deleted file mode 100644 index 37a1f8d..0000000 --- a/src/openboost/_models/_sklearn.py +++ /dev/null @@ -1,1749 +0,0 @@ -"""sklearn-compatible wrappers for OpenBoost models. - -Phase 13: Thin adapters that provide sklearn compatibility -(GridSearchCV, cross_val_score, Pipeline, etc.). - -These wrappers delegate to the core OpenBoost models while providing: -- sklearn BaseEstimator interface (get_params, set_params) -- RegressorMixin / ClassifierMixin (score method) -- Proper input validation (check_X_y, check_array) -- Feature importance as a property - -Example: - ```python - from openboost import OpenBoostRegressor, OpenBoostClassifier - from sklearn.model_selection import GridSearchCV - - # Regression - reg = OpenBoostRegressor(n_estimators=100, max_depth=6) - reg.fit(X_train, y_train) - reg.score(X_test, y_test) # R² score - - # Classification - clf = OpenBoostClassifier(n_estimators=100) - clf.fit(X_train, y_train) - clf.predict_proba(X_test) - ``` - >>> clf.classes_ - >>> - >>> # GridSearchCV - >>> param_grid = {'n_estimators': [50, 100], 'max_depth': [3, 5, 7]} - >>> search = GridSearchCV(OpenBoostRegressor(), param_grid, cv=5) - >>> search.fit(X, y) -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Literal - -import numpy as np - -try: - from sklearn.base import BaseEstimator, ClassifierMixin, RegressorMixin - from sklearn.preprocessing import LabelEncoder - from sklearn.utils.validation import check_array, check_is_fitted, check_X_y - SKLEARN_AVAILABLE = True -except ImportError: - SKLEARN_AVAILABLE = False - # Provide stubs if sklearn not available - class BaseEstimator: - pass - class RegressorMixin: - pass - class ClassifierMixin: - pass - -from .._callbacks import EarlyStopping -from .._importance import compute_feature_importances -from ._boosting import GradientBoosting, MultiClassGradientBoosting -from ._dart import DART -from ._gam import OpenBoostGAM - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -def _check_sklearn(): - """Raise error if sklearn not available.""" - if not SKLEARN_AVAILABLE: - raise ImportError( - "sklearn is required for OpenBoostRegressor/OpenBoostClassifier. " - "Install with: pip install scikit-learn" - ) - - -class OpenBoostRegressor(BaseEstimator, RegressorMixin): - """Gradient Boosting Regressor with sklearn-compatible interface. - - This is a thin wrapper around OpenBoost's GradientBoosting that provides - full compatibility with sklearn's ecosystem (GridSearchCV, Pipeline, etc.). - - Parameters - ---------- - n_estimators : int, default=100 - Number of boosting rounds (trees). - max_depth : int, default=6 - Maximum depth of each tree. - learning_rate : float, default=0.1 - Shrinkage factor applied to each tree's contribution. - loss : {'squared_error', 'absolute_error', 'huber', 'quantile'}, default='squared_error' - Loss function to optimize. - min_child_weight : float, default=1.0 - Minimum sum of hessian in a leaf node. - reg_lambda : float, default=1.0 - L2 regularization on leaf values. - reg_alpha : float, default=0.0 - L1 regularization on leaf values. - gamma : float, default=0.0 - Minimum gain required to make a split. - subsample : float, default=1.0 - Fraction of samples to use for each tree. - colsample_bytree : float, default=1.0 - Fraction of features to use for each tree. - n_bins : int, default=254 - Number of bins for histogram building. - quantile_alpha : float, default=0.5 - Quantile level for 'quantile' loss. - subsample_strategy : {'none', 'random', 'goss'}, default='none' - Sampling strategy for large-scale training (Phase 17). - - 'none': No sampling (default) - - 'random': Random subsampling - - 'goss': Gradient-based One-Side Sampling (LightGBM-style) - goss_top_rate : float, default=0.2 - Fraction of top-gradient samples to keep (for GOSS). - goss_other_rate : float, default=0.1 - Fraction of remaining samples to sample (for GOSS). - batch_size : int, optional - Mini-batch size for large datasets. If None, process all at once. - early_stopping_rounds : int, optional - Stop training if validation score doesn't improve for this many rounds. - Requires eval_set to be passed to fit(). - verbose : int, default=0 - Verbosity level (0=silent, N=log every N rounds). - random_state : int, optional - Random seed for reproducibility. - - Attributes - ---------- - n_features_in_ : int - Number of features seen during fit. - feature_names_in_ : ndarray of shape (n_features_in_,) - Names of features seen during fit (if X is a DataFrame). - feature_importances_ : ndarray of shape (n_features_in_,) - Feature importances (based on split frequency). - booster_ : GradientBoosting - The underlying fitted OpenBoost model. - best_iteration_ : int - Iteration with best validation score (if early stopping used). - best_score_ : float - Best validation score achieved (if early stopping used). - - Examples - -------- - >>> from openboost import OpenBoostRegressor - >>> reg = OpenBoostRegressor(n_estimators=100, max_depth=6) - >>> reg.fit(X_train, y_train) - >>> reg.predict(X_test) - >>> reg.score(X_test, y_test) # R² score - - >>> # With early stopping - >>> reg = OpenBoostRegressor(n_estimators=1000, early_stopping_rounds=50) - >>> reg.fit(X_train, y_train, eval_set=[(X_val, y_val)]) - >>> print(f"Best iteration: {reg.best_iteration_}") - - >>> # GridSearchCV - >>> from sklearn.model_selection import GridSearchCV - >>> param_grid = {'n_estimators': [50, 100], 'max_depth': [3, 5]} - >>> search = GridSearchCV(OpenBoostRegressor(), param_grid, cv=5) - >>> search.fit(X, y) - """ - - def __init__( - self, - n_estimators: int = 100, - max_depth: int = 6, - learning_rate: float = 0.1, - loss: Literal['squared_error', 'absolute_error', 'huber', 'quantile'] = 'squared_error', - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, - gamma: float = 0.0, - subsample: float = 1.0, - colsample_bytree: float = 1.0, - n_bins: int = 254, - quantile_alpha: float = 0.5, - subsample_strategy: Literal['none', 'random', 'goss'] = 'none', - goss_top_rate: float = 0.2, - goss_other_rate: float = 0.1, - batch_size: int | None = None, - early_stopping_rounds: int | None = None, - verbose: int = 0, - random_state: int | None = None, - ) -> None: - self.n_estimators = n_estimators - self.max_depth = max_depth - self.learning_rate = learning_rate - self.loss = loss - self.min_child_weight = min_child_weight - self.reg_lambda = reg_lambda - self.reg_alpha = reg_alpha - self.gamma = gamma - self.subsample = subsample - self.colsample_bytree = colsample_bytree - self.n_bins = n_bins - self.quantile_alpha = quantile_alpha - self.subsample_strategy = subsample_strategy - self.goss_top_rate = goss_top_rate - self.goss_other_rate = goss_other_rate - self.batch_size = batch_size - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - self.random_state = random_state - - def fit( - self, - X: NDArray, - y: NDArray, - sample_weight: NDArray | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ) -> OpenBoostRegressor: - """Fit the gradient boosting regressor. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Training features. - y : array-like of shape (n_samples,) - Target values. - sample_weight : array-like of shape (n_samples,), optional - Sample weights. - eval_set : list of (X, y) tuples, optional - Validation sets for early stopping. - - Returns - ------- - self : OpenBoostRegressor - Fitted estimator. - """ - _check_sklearn() - - # Validate input - X, y = check_X_y(X, y, dtype=np.float32, y_numeric=True) - - # Store sklearn attributes - self.n_features_in_ = X.shape[1] - - # Map sklearn loss names to OpenBoost names - loss_map = { - 'squared_error': 'mse', - 'absolute_error': 'mae', - 'huber': 'huber', - 'quantile': 'quantile', - } - internal_loss = loss_map.get(self.loss, self.loss) - - # Build callback list - callbacks = [] - if self.early_stopping_rounds is not None and eval_set is not None: - callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, - restore_best=True, - verbose=self.verbose > 0, - )) - - # Create core model - self.booster_ = GradientBoosting( - n_trees=self.n_estimators, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - loss=internal_loss, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - n_bins=self.n_bins, - quantile_alpha=self.quantile_alpha, - # Phase 17: Large-scale training - subsample_strategy=self.subsample_strategy, - goss_top_rate=self.goss_top_rate, - goss_other_rate=self.goss_other_rate, - batch_size=self.batch_size, - random_state=self.random_state, - ) - - # Fit with callbacks - self.booster_.fit( - X, y, - callbacks=callbacks if callbacks else None, - eval_set=eval_set, - sample_weight=sample_weight, - ) - - # Copy early stopping attributes - if hasattr(self.booster_, 'best_iteration_'): - self.best_iteration_ = self.booster_.best_iteration_ - if hasattr(self.booster_, 'best_score_'): - self.best_score_ = self.booster_.best_score_ - - # LOW-11: Cache feature importances at fit time - self._cached_feature_importances = compute_feature_importances( - self.booster_, importance_type='frequency' - ) - - return self - - def predict(self, X: NDArray) -> NDArray: - """Predict target values. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - - Returns - ------- - y_pred : ndarray of shape (n_samples,) - Predicted values. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict(X) - - @property - def feature_importances_(self) -> NDArray: - """Feature importances based on split frequency.""" - check_is_fitted(self, 'booster_') - return self._cached_feature_importances - - # score() is inherited from RegressorMixin (R² score) - - -class OpenBoostClassifier(BaseEstimator, ClassifierMixin): - """Gradient Boosting Classifier with sklearn-compatible interface. - - Automatically handles binary and multi-class classification. - Uses logloss for binary, softmax for multi-class. - - Parameters - ---------- - n_estimators : int, default=100 - Number of boosting rounds. - max_depth : int, default=6 - Maximum depth of each tree. - learning_rate : float, default=0.1 - Shrinkage factor. - min_child_weight : float, default=1.0 - Minimum sum of hessian in a leaf. - reg_lambda : float, default=1.0 - L2 regularization on leaf values. - reg_alpha : float, default=0.0 - L1 regularization on leaf values. - gamma : float, default=0.0 - Minimum gain required to make a split. - subsample : float, default=1.0 - Fraction of samples per tree. - colsample_bytree : float, default=1.0 - Fraction of features per tree. - n_bins : int, default=254 - Number of bins for histogram building. - subsample_strategy : {'none', 'random', 'goss'}, default='none' - Sampling strategy for large-scale training (Phase 17). - goss_top_rate : float, default=0.2 - Fraction of top-gradient samples to keep (for GOSS). - goss_other_rate : float, default=0.1 - Fraction of remaining samples to sample (for GOSS). - batch_size : int, optional - Mini-batch size for large datasets. - early_stopping_rounds : int, optional - Stop if validation doesn't improve. - verbose : int, default=0 - Verbosity level. - random_state : int, optional - Random seed. - - Attributes - ---------- - classes_ : ndarray - Unique class labels. - n_classes_ : int - Number of classes. - n_features_in_ : int - Number of features. - feature_importances_ : ndarray - Feature importances. - booster_ : GradientBoosting or MultiClassGradientBoosting - Underlying model. - - Examples - -------- - >>> from openboost import OpenBoostClassifier - >>> clf = OpenBoostClassifier(n_estimators=100) - >>> clf.fit(X_train, y_train) - >>> clf.predict(X_test) - >>> clf.predict_proba(X_test) - >>> clf.classes_ - array([0, 1]) - - >>> # Multi-class - >>> clf.fit(X_train, y_train) # y_train has 3+ classes - >>> clf.predict_proba(X_test).shape - (n_samples, n_classes) - """ - - def __init__( - self, - n_estimators: int = 100, - max_depth: int = 6, - learning_rate: float = 0.1, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - reg_alpha: float = 0.0, - gamma: float = 0.0, - subsample: float = 1.0, - colsample_bytree: float = 1.0, - n_bins: int = 254, - subsample_strategy: Literal['none', 'random', 'goss'] = 'none', - goss_top_rate: float = 0.2, - goss_other_rate: float = 0.1, - batch_size: int | None = None, - early_stopping_rounds: int | None = None, - verbose: int = 0, - random_state: int | None = None, - ) -> None: - self.n_estimators = n_estimators - self.max_depth = max_depth - self.learning_rate = learning_rate - self.min_child_weight = min_child_weight - self.reg_lambda = reg_lambda - self.reg_alpha = reg_alpha - self.gamma = gamma - self.subsample = subsample - self.colsample_bytree = colsample_bytree - self.n_bins = n_bins - self.subsample_strategy = subsample_strategy - self.goss_top_rate = goss_top_rate - self.goss_other_rate = goss_other_rate - self.batch_size = batch_size - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - self.random_state = random_state - - def fit( - self, - X: NDArray, - y: NDArray, - sample_weight: NDArray | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ) -> OpenBoostClassifier: - """Fit the gradient boosting classifier. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Training features. - y : array-like of shape (n_samples,) - Target class labels. - sample_weight : array-like of shape (n_samples,), optional - Sample weights. - eval_set : list of (X, y) tuples, optional - Validation sets for early stopping. - - Returns - ------- - self : OpenBoostClassifier - Fitted estimator. - """ - _check_sklearn() - - # Validate input - X, y = check_X_y(X, y, dtype=np.float32) - - # Store sklearn attributes - self.n_features_in_ = X.shape[1] - - # Encode labels - self._label_encoder = LabelEncoder() - y_encoded = self._label_encoder.fit_transform(y) - self.classes_ = self._label_encoder.classes_ - self.n_classes_ = len(self.classes_) - - # Transform eval_set labels if provided - if eval_set is not None: - eval_set_encoded = [] - for X_val, y_val in eval_set: - y_val_encoded = self._label_encoder.transform(y_val) - eval_set_encoded.append((X_val, y_val_encoded)) - eval_set = eval_set_encoded - - # Build callbacks - callbacks = [] - if self.early_stopping_rounds is not None and eval_set is not None: - callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, - restore_best=True, - verbose=self.verbose > 0, - )) - - # Choose model based on number of classes - if self.n_classes_ == 2: - # Binary classification - self.booster_ = GradientBoosting( - n_trees=self.n_estimators, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - loss='logloss', - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - n_bins=self.n_bins, - # Phase 17: Large-scale training - subsample_strategy=self.subsample_strategy, - goss_top_rate=self.goss_top_rate, - goss_other_rate=self.goss_other_rate, - batch_size=self.batch_size, - random_state=self.random_state, - ) - self.booster_.fit( - X, y_encoded.astype(np.float32), - callbacks=callbacks if callbacks else None, - eval_set=eval_set, - sample_weight=sample_weight, - ) - else: - # Multi-class classification - self.booster_ = MultiClassGradientBoosting( - n_classes=self.n_classes_, - n_trees=self.n_estimators, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - gamma=self.gamma, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - n_bins=self.n_bins, - # Phase 17: Large-scale training - subsample_strategy=self.subsample_strategy, - goss_top_rate=self.goss_top_rate, - goss_other_rate=self.goss_other_rate, - batch_size=self.batch_size, - ) - import warnings - if sample_weight is not None: - warnings.warn( - "MultiClassGradientBoosting does not yet support " - "sample_weight. This argument is ignored.", - UserWarning, - stacklevel=2, - ) - self.booster_.fit( - X, y_encoded, - callbacks=callbacks if callbacks else None, - eval_set=eval_set, - ) - - # Copy early stopping attributes - if hasattr(self.booster_, 'best_iteration_'): - self.best_iteration_ = self.booster_.best_iteration_ - if hasattr(self.booster_, 'best_score_'): - self.best_score_ = self.booster_.best_score_ - - # LOW-11: Cache feature importances at fit time - self._cached_feature_importances = compute_feature_importances( - self.booster_, importance_type='frequency' - ) - - return self - - def predict(self, X: NDArray) -> NDArray: - """Predict class labels. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - - Returns - ------- - y_pred : ndarray of shape (n_samples,) - Predicted class labels. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - proba = self.predict_proba(X) - indices = np.argmax(proba, axis=1) - return self.classes_[indices] - - def predict_proba(self, X: NDArray) -> NDArray: - """Predict class probabilities. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - - Returns - ------- - proba : ndarray of shape (n_samples, n_classes) - Class probabilities. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict_proba(X) - - @property - def feature_importances_(self) -> NDArray: - """Feature importances based on split frequency.""" - check_is_fitted(self, 'booster_') - return self._cached_feature_importances - - # score() is inherited from ClassifierMixin (accuracy) - - -# ============================================================================= -# Phase 15: Distributional Regressor (NGBoost-style) -# ============================================================================= - -class OpenBoostDistributionalRegressor(BaseEstimator, RegressorMixin): - """Distributional regression with sklearn-compatible interface. - - Predicts full probability distributions instead of point estimates. - Uses natural gradient boosting (NGBoost) by default for faster convergence. - - Parameters - ---------- - distribution : str, default='normal' - Distribution family. Options: 'normal', 'lognormal', 'gamma', - 'poisson', 'studentt', 'tweedie', 'negbin'. - n_estimators : int, default=100 - Number of boosting rounds. - max_depth : int, default=4 - Maximum depth of each tree. Typically shallower than standard GBDT. - learning_rate : float, default=0.1 - Shrinkage factor. - min_child_weight : float, default=1.0 - Minimum sum of hessian in a leaf. - reg_lambda : float, default=1.0 - L2 regularization on leaf values. - n_bins : int, default=254 - Number of bins for histogram building. - use_natural_gradient : bool, default=True - If True, use NGBoost (natural gradient). Recommended for faster - convergence and better uncertainty calibration. - early_stopping_rounds : int, optional - Stop training if the validation metric (``eval_metric``) doesn't - improve for this many rounds. Requires eval_set to be passed to - fit(). With multiple eval sets, the last one is monitored. - verbose : int, default=0 - Verbosity level. - eval_metric : {'nll', 'crps', 'pinball', 'interval_score'}, default='nll' - Metric computed on each eval set every round and recorded in - ``evals_result_``. - quantiles : list of float, optional - Quantile levels used by eval_metric='pinball'. - Defaults to [0.05, 0.5, 0.95]. - interval_alpha : float, default=0.1 - Miscoverage rate used by eval_metric='interval_score' - (0.1 scores the central 90% interval). - - Attributes - ---------- - n_features_in_ : int - Number of features seen during fit. - booster_ : NaturalBoost or DistributionalGBDT - The underlying fitted model. - evals_result_ : dict - Per-round eval-set metric history recorded during fit(), e.g. - ``{'eval_0': {'nll': [...]}}``. - best_iteration_ : int - Iteration with best validation score (if early stopping used). - best_score_ : float - Best validation score achieved (if early stopping used). - - Examples - -------- - >>> from openboost import OpenBoostDistributionalRegressor - >>> model = OpenBoostDistributionalRegressor(distribution='normal') - >>> model.fit(X_train, y_train) - >>> - >>> # Point prediction (mean) - >>> y_pred = model.predict(X_test) - >>> - >>> # Prediction intervals (90%) - >>> lower, upper = model.predict_interval(X_test, alpha=0.1) - >>> - >>> # Full distribution parameters - >>> params = model.predict_distribution(X_test) - >>> mu, sigma = params['loc'], params['scale'] - >>> - >>> # Sample from predicted distribution - >>> samples = model.sample(X_test, n_samples=100) - """ - - def __init__( - self, - distribution: Literal[ - 'normal', 'lognormal', 'gamma', 'poisson', 'studentt', 'tweedie', 'negbin' - ] = 'normal', - n_estimators: int = 100, - max_depth: int = 4, - learning_rate: float = 0.1, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - n_bins: int = 254, - use_natural_gradient: bool = True, - early_stopping_rounds: int | None = None, - verbose: int = 0, - eval_metric: Literal['nll', 'crps', 'pinball', 'interval_score'] = 'nll', - quantiles: list[float] | None = None, - interval_alpha: float = 0.1, - ) -> None: - self.distribution = distribution - self.n_estimators = n_estimators - self.max_depth = max_depth - self.learning_rate = learning_rate - self.min_child_weight = min_child_weight - self.reg_lambda = reg_lambda - self.n_bins = n_bins - self.use_natural_gradient = use_natural_gradient - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - self.eval_metric = eval_metric - self.quantiles = quantiles - self.interval_alpha = interval_alpha - - def fit( - self, - X: NDArray, - y: NDArray, - sample_weight: NDArray | None = None, - exposure: NDArray | None = None, - eval_set: list[tuple] | None = None, - callbacks: list | None = None, - ) -> OpenBoostDistributionalRegressor: - """Fit the distributional regressor. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Training features. - y : array-like of shape (n_samples,) - Target values. - sample_weight : array-like of shape (n_samples,), optional - Non-negative per-sample weights. The training objective becomes - the weighted sum of per-sample NLL. - exposure : float or array-like of shape (n_samples,), optional - Strictly positive per-sample exposure for families with a - log-link mean (Poisson, NegativeBinomial, Gamma, Tweedie): the - mean is modeled as ``exposure * exp(raw_score)``. Raises - ValueError for families without a log-link mean. - eval_set : list of tuples, optional - Validation sets as (X, y) tuples, or (X, y, exposure) 3-tuples - for exposure-aware validation. Each set is evaluated every round - with ``eval_metric`` and the history is stored in - ``evals_result_``. - callbacks : list of Callback, optional - Callbacks forwarded to the underlying booster's fit(). - - Returns - ------- - self : OpenBoostDistributionalRegressor - Fitted estimator. - """ - _check_sklearn() - X, y = check_X_y(X, y, dtype=np.float32, y_numeric=True) - - self.n_features_in_ = X.shape[1] - - # Import here to avoid circular imports - from ._distributional import DistributionalGBDT, NaturalBoost - - ModelClass = NaturalBoost if self.use_natural_gradient else DistributionalGBDT - - self.booster_ = ModelClass( - distribution=self.distribution, - n_trees=self.n_estimators, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - n_bins=self.n_bins, - ) - - all_callbacks = list(callbacks) if callbacks else [] - if self.early_stopping_rounds is not None and eval_set is not None: - all_callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, - restore_best=True, - verbose=self.verbose > 0, - )) - - self.booster_.fit( - X, y, - sample_weight=sample_weight, - exposure=exposure, - callbacks=all_callbacks if all_callbacks else None, - eval_set=eval_set, - eval_metric=self.eval_metric, - quantiles=self.quantiles, - interval_alpha=self.interval_alpha, - ) - - return self - - @property - def evals_result_(self) -> dict[str, dict[str, list[float]]]: - """Per-round eval-set metric history recorded during fit().""" - check_is_fitted(self, 'booster_') - return self.booster_.evals_result_ - - @property - def best_iteration_(self) -> int: - """Best round index (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_iteration_ - - @property - def best_score_(self) -> float: - """Best monitored metric value (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_score_ - - def predict(self, X: NDArray, exposure: NDArray | None = None) -> NDArray: - """Predict mean (expected value). - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - exposure : float or array-like of shape (n_samples,), optional - Strictly positive per-sample exposure for log-link-mean - families; None means exposure 1. - - Returns - ------- - y_pred : ndarray of shape (n_samples,) - Predicted mean values. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict(X, exposure=exposure) - - def predict_interval( - self, - X: NDArray, - alpha: float = 0.1, - exposure: NDArray | None = None, - ) -> tuple[NDArray, NDArray]: - """Predict (1-alpha) prediction interval. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - alpha : float, default=0.1 - Significance level. 0.1 gives a 90% prediction interval. - exposure : float or array-like of shape (n_samples,), optional - Per-sample exposure (see ``predict``). - - Returns - ------- - lower : ndarray of shape (n_samples,) - Lower bounds of the interval. - upper : ndarray of shape (n_samples,) - Upper bounds of the interval. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict_interval(X, alpha=alpha, exposure=exposure) - - def predict_distribution( - self, - X: NDArray, - exposure: NDArray | None = None, - ) -> dict[str, NDArray]: - """Predict all distribution parameters. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - exposure : float or array-like of shape (n_samples,), optional - Per-sample exposure (see ``predict``). - - Returns - ------- - params : dict - Dictionary mapping parameter names to predicted values. - For Normal: {'loc': mean, 'scale': std} - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict_params(X, exposure=exposure) - - def predict_quantile( - self, - X: NDArray, - q: float, - exposure: NDArray | None = None, - ) -> NDArray: - """Predict q-th quantile. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - q : float - Quantile level (0 < q < 1). - exposure : float or array-like of shape (n_samples,), optional - Per-sample exposure (see ``predict``). - - Returns - ------- - quantiles : ndarray of shape (n_samples,) - Predicted quantiles. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict_quantile(X, q, exposure=exposure) - - def sample( - self, - X: NDArray, - n_samples: int = 1, - seed: int | None = None, - exposure: NDArray | None = None, - ) -> NDArray: - """Sample from predicted distribution. - - Parameters - ---------- - X : array-like of shape (n_obs, n_features) - Features to predict on. - n_samples : int, default=1 - Number of samples per observation. - seed : int, optional - Random seed for reproducibility. - exposure : float or array-like of shape (n_obs,), optional - Per-sample exposure (see ``predict``). - - Returns - ------- - samples : ndarray of shape (n_obs, n_samples) - Samples from the predicted distribution. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.sample(X, n_samples, seed, exposure=exposure) - - def nll_score( - self, - X: NDArray, - y: NDArray, - exposure: NDArray | None = None, - ) -> float: - """Compute negative log-likelihood (lower is better). - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features. - y : array-like of shape (n_samples,) - True target values. - exposure : float or array-like of shape (n_samples,), optional - Per-sample exposure (see ``predict``). - - Returns - ------- - nll : float - Mean negative log-likelihood. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.nll(X, y, exposure=exposure) - - # score() inherited from RegressorMixin uses R² on mean predictions - - -# ============================================================================= -# Phase 15: Linear Leaf Regressor -# ============================================================================= - -class OpenBoostLinearLeafRegressor(BaseEstimator, RegressorMixin): - """Linear Leaf Gradient Boosting with sklearn-compatible interface. - - Uses trees with linear models in leaves instead of constant values. - This provides better extrapolation beyond the training data range. - - Parameters - ---------- - n_estimators : int, default=100 - Number of boosting rounds. - max_depth : int, default=4 - Maximum tree depth. Typically shallower than standard GBDT since - linear models in leaves add flexibility. - learning_rate : float, default=0.1 - Shrinkage factor. - loss : str, default='squared_error' - Loss function: 'squared_error', 'absolute_error', 'huber'. - min_samples_leaf : int, default=20 - Minimum samples in a leaf to fit linear model. - reg_lambda : float, default=1.0 - L2 regularization for tree splits. - reg_lambda_linear : float, default=0.1 - L2 regularization for linear models in leaves (ridge). - max_features_linear : int, str, or None, default='sqrt' - Max features for linear model in each leaf: - - None: Use all features - - 'sqrt': Use sqrt(n_features) - - 'log2': Use log2(n_features) - - int: Use exactly this many features - n_bins : int, default=254 - Number of bins for histogram building. - early_stopping_rounds : int, optional - Stop training if the validation MSE doesn't improve for this many - rounds. Requires eval_set to be passed to fit(). With multiple eval - sets, the last one is monitored. - verbose : int, default=0 - Verbosity level. - - Attributes - ---------- - n_features_in_ : int - Number of features seen during fit. - booster_ : LinearLeafGBDT - The underlying fitted model. - evals_result_ : dict - Per-round eval-set MSE history recorded during fit(), e.g. - ``{'eval_0': {'mse': [...]}}``. Empty dict when no eval_set was used. - best_iteration_ : int - Iteration with best validation score (if early stopping used). - best_score_ : float - Best validation score achieved (if early stopping used). - - Examples - -------- - >>> from openboost import OpenBoostLinearLeafRegressor - >>> model = OpenBoostLinearLeafRegressor(n_estimators=100, max_depth=4) - >>> model.fit(X_train, y_train) - >>> y_pred = model.predict(X_test) - >>> - >>> # Compare with standard GBDT on extrapolation tasks - >>> # LinearLeafRegressor typically performs better when the - >>> # underlying relationship has linear components - """ - - def __init__( - self, - n_estimators: int = 100, - max_depth: int = 4, - learning_rate: float = 0.1, - loss: Literal['squared_error', 'absolute_error', 'huber'] = 'squared_error', - min_samples_leaf: int = 20, - reg_lambda: float = 1.0, - reg_lambda_linear: float = 0.1, - max_features_linear: int | Literal['sqrt', 'log2'] | None = 'sqrt', - n_bins: int = 254, - early_stopping_rounds: int | None = None, - verbose: int = 0, - ) -> None: - self.n_estimators = n_estimators - self.max_depth = max_depth - self.learning_rate = learning_rate - self.loss = loss - self.min_samples_leaf = min_samples_leaf - self.reg_lambda = reg_lambda - self.reg_lambda_linear = reg_lambda_linear - self.max_features_linear = max_features_linear - self.n_bins = n_bins - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - - def fit( - self, - X: NDArray, - y: NDArray, - sample_weight: NDArray | None = None, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - callbacks: list | None = None, - ) -> OpenBoostLinearLeafRegressor: - """Fit the linear leaf regressor. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Training features. - y : array-like of shape (n_samples,) - Target values. - sample_weight : array-like, optional - Not supported; raises NotImplementedError if passed. - eval_set : list of (X, y) tuples, optional - Validation set(s) scored with MSE every round; history is stored - in ``evals_result_``. The LAST eval set is monitored by - callbacks / early stopping. - callbacks : list of Callback, optional - Callbacks forwarded to the underlying booster's fit(). - - Returns - ------- - self : OpenBoostLinearLeafRegressor - Fitted estimator. - """ - _check_sklearn() - if sample_weight is not None: - raise NotImplementedError( - "sample_weight is not yet supported for OpenBoostLinearLeafRegressor" - ) - X, y = check_X_y(X, y, dtype=np.float32, y_numeric=True) - - self.n_features_in_ = X.shape[1] - - # Map sklearn loss names to internal names - loss_map = { - 'squared_error': 'mse', - 'absolute_error': 'mae', - 'huber': 'huber', - } - internal_loss = loss_map.get(self.loss, self.loss) - - from ._linear_leaf import LinearLeafGBDT - - self.booster_ = LinearLeafGBDT( - n_trees=self.n_estimators, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - loss=internal_loss, - min_samples_leaf=self.min_samples_leaf, - reg_lambda_tree=self.reg_lambda, - reg_lambda_linear=self.reg_lambda_linear, - max_features_linear=self.max_features_linear, - n_bins=self.n_bins, - ) - - all_callbacks = list(callbacks) if callbacks else [] - if self.early_stopping_rounds is not None and eval_set is not None: - all_callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, - restore_best=True, - verbose=self.verbose > 0, - )) - - self.booster_.fit( - X, y, - callbacks=all_callbacks if all_callbacks else None, - eval_set=eval_set, - ) - return self - - @property - def evals_result_(self) -> dict[str, dict[str, list[float]]]: - """Per-round eval-set MSE history recorded during fit().""" - check_is_fitted(self, 'booster_') - return self.booster_.evals_result_ - - @property - def best_iteration_(self) -> int: - """Best round index (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_iteration_ - - @property - def best_score_(self) -> float: - """Best monitored metric value (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_score_ - - def predict(self, X: NDArray) -> NDArray: - """Predict target values. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - - Returns - ------- - y_pred : ndarray of shape (n_samples,) - Predicted values. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - - return self.booster_.predict(X) - - # score() inherited from RegressorMixin (R² score) - - -class OpenBoostDARTRegressor(BaseEstimator, RegressorMixin): - """DART Regressor with sklearn-compatible interface. - - Wraps :class:`DART` for use with GridSearchCV, Pipeline, etc. - - Parameters - ---------- - n_estimators : int, default=100 - Number of boosting rounds. - max_depth : int, default=6 - Maximum tree depth. - learning_rate : float, default=0.1 - Shrinkage factor. - loss : str, default='squared_error' - Loss function. 'squared_error', 'absolute_error', 'huber'. - dropout_rate : float, default=0.1 - Probability of dropping a tree each round. - skip_drop : float, default=0.0 - Probability of skipping dropout entirely. - normalize : bool, default=True - Normalize tree weights after dropout. - min_child_weight : float, default=1.0 - Minimum sum of hessian in a leaf. - reg_lambda : float, default=1.0 - L2 regularization. - n_bins : int, default=254 - Number of histogram bins. - early_stopping_rounds : int or None, default=None - Stop if validation loss doesn't improve for this many rounds. - verbose : int, default=0 - Logging verbosity. - random_state : int or None, default=None - Random seed for reproducibility. - """ - - def __init__( - self, - n_estimators: int = 100, - max_depth: int = 6, - learning_rate: float = 0.1, - loss: Literal['squared_error', 'absolute_error', 'huber'] = 'squared_error', - dropout_rate: float = 0.1, - skip_drop: float = 0.0, - normalize: bool = True, - min_child_weight: float = 1.0, - reg_lambda: float = 1.0, - n_bins: int = 254, - early_stopping_rounds: int | None = None, - verbose: int = 0, - random_state: int | None = None, - ) -> None: - self.n_estimators = n_estimators - self.max_depth = max_depth - self.learning_rate = learning_rate - self.loss = loss - self.dropout_rate = dropout_rate - self.skip_drop = skip_drop - self.normalize = normalize - self.min_child_weight = min_child_weight - self.reg_lambda = reg_lambda - self.n_bins = n_bins - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - self.random_state = random_state - - def fit( - self, - X: NDArray, - y: NDArray, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - ) -> OpenBoostDARTRegressor: - """Fit the DART regressor.""" - _check_sklearn() - X, y = check_X_y(X, y, dtype=np.float32, y_numeric=True) - self.n_features_in_ = X.shape[1] - - loss_map = {'squared_error': 'mse', 'absolute_error': 'mae', 'huber': 'huber'} - internal_loss = loss_map.get(self.loss, self.loss) - - self.booster_ = DART( - n_trees=self.n_estimators, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - loss=internal_loss, - dropout_rate=self.dropout_rate, - skip_drop=self.skip_drop, - normalize=self.normalize, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - n_bins=self.n_bins, - random_state=self.random_state, - ) - - callbacks = [] - if self.early_stopping_rounds is not None: - callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, restore_best=True, - )) - if self.verbose > 0: - from .._callbacks import Logger - callbacks.append(Logger(period=max(1, self.n_estimators // 10))) - - self.booster_.fit( - X, y, - callbacks=callbacks if callbacks else None, - eval_set=eval_set, - ) - - if hasattr(self.booster_, 'best_iteration_'): - self.best_iteration_ = self.booster_.best_iteration_ - if hasattr(self.booster_, 'best_score_'): - self.best_score_ = self.booster_.best_score_ - - self._cached_feature_importances = compute_feature_importances( - self.booster_, importance_type='frequency' - ) - return self - - def predict(self, X: NDArray) -> NDArray: - """Predict target values.""" - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - return self.booster_.predict(X) - - @property - def feature_importances_(self) -> NDArray: - """Feature importances (frequency-based).""" - check_is_fitted(self, '_cached_feature_importances') - return self._cached_feature_importances - - # score() inherited from RegressorMixin (R² score) - - -class OpenBoostGAMRegressor(BaseEstimator, RegressorMixin): - """GAM Regressor with sklearn-compatible interface. - - Wraps :class:`OpenBoostGAM` for use with GridSearchCV, Pipeline, etc. - - Parameters - ---------- - n_estimators : int, default=1000 - Number of boosting rounds. - learning_rate : float, default=0.01 - Shrinkage factor. - reg_lambda : float, default=1.0 - L2 regularization. - loss : str, default='squared_error' - Loss function. 'squared_error' or 'absolute_error'. - n_bins : int, default=254 - Number of histogram bins. - interactions : int, default=0 - Number of pairwise interaction terms (GA2M-style) learned after - main-effects training. 0 disables interactions. - interaction_rounds : int, optional - Boosting rounds for the interaction stage. None uses n_estimators. - smoothing : float, default=0.0 - Fused-ridge smoothing strength for 1D shape functions (0 = off). - monotone : dict, optional - Mapping of feature index -> +1 (non-decreasing) or -1 - (non-increasing) monotonicity constraint. - early_stopping_rounds : int, optional - Stop training if the validation loss doesn't improve for this many - rounds. Requires eval_set to be passed to fit(). With multiple eval - sets, the last one is monitored. - verbose : int, default=0 - Logging verbosity. - - Attributes - ---------- - n_features_in_ : int - Number of features seen during fit. - booster_ : OpenBoostGAM - The underlying fitted model. - shape_values_ : ndarray - 1D shape function lookup tables from the underlying GAM. - interaction_pairs_ : list of tuple - Selected interaction pairs (ranked), when interactions > 0. - evals_result_ : dict - Per-round eval-set metric history recorded during fit(), e.g. - ``{'eval_0': {'mse': [...]}}``. Empty dict when no eval_set was used. - best_iteration_ : int - Iteration with best validation score (if early stopping used). - best_score_ : float - Best validation score achieved (if early stopping used). - """ - - def __init__( - self, - n_estimators: int = 1000, - learning_rate: float = 0.01, - reg_lambda: float = 1.0, - loss: Literal['squared_error', 'absolute_error'] = 'squared_error', - n_bins: int = 254, - interactions: int = 0, - interaction_rounds: int | None = None, - smoothing: float = 0.0, - monotone: dict[int, int] | None = None, - early_stopping_rounds: int | None = None, - verbose: int = 0, - ) -> None: - self.n_estimators = n_estimators - self.learning_rate = learning_rate - self.reg_lambda = reg_lambda - self.loss = loss - self.n_bins = n_bins - self.interactions = interactions - self.interaction_rounds = interaction_rounds - self.smoothing = smoothing - self.monotone = monotone - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - - def fit( - self, - X: NDArray, - y: NDArray, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - callbacks: list | None = None, - ) -> OpenBoostGAMRegressor: - """Fit the GAM regressor. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Training features. - y : array-like of shape (n_samples,) - Target values. - eval_set : list of (X, y) tuples, optional - Validation set(s) scored with the training loss every round; - history is stored in ``evals_result_``. The LAST eval set is - monitored by callbacks / early stopping. - callbacks : list of Callback, optional - Callbacks forwarded to the underlying booster's fit(). - - Returns - ------- - self : OpenBoostGAMRegressor - Fitted estimator. - """ - _check_sklearn() - X, y = check_X_y(X, y, dtype=np.float32, y_numeric=True) - self.n_features_in_ = X.shape[1] - - loss_map = {'squared_error': 'mse', 'absolute_error': 'mae'} - internal_loss = loss_map.get(self.loss, self.loss) - - self.booster_ = OpenBoostGAM( - n_trees=self.n_estimators, - learning_rate=self.learning_rate, - reg_lambda=self.reg_lambda, - loss=internal_loss, - n_bins=self.n_bins, - interactions=self.interactions, - interaction_rounds=self.interaction_rounds, - smoothing=self.smoothing, - monotone=self.monotone, - ) - - all_callbacks = list(callbacks) if callbacks else [] - if self.early_stopping_rounds is not None and eval_set is not None: - all_callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, - restore_best=True, - verbose=self.verbose > 0, - )) - - self.booster_.fit( - X, y, - callbacks=all_callbacks if all_callbacks else None, - eval_set=eval_set, - ) - return self - - def predict(self, X: NDArray) -> NDArray: - """Predict target values.""" - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - return self.booster_.predict(X) - - @property - def shape_values_(self) -> NDArray: - """Shape function values from the underlying GAM.""" - check_is_fitted(self, 'booster_') - return self.booster_.shape_values_ - - @property - def interaction_pairs_(self) -> list[tuple[int, int]]: - """Selected interaction pairs (ranked) from the underlying GAM.""" - check_is_fitted(self, 'booster_') - return self.booster_.interaction_pairs_ - - @property - def evals_result_(self) -> dict[str, dict[str, list[float]]]: - """Per-round eval-set metric history recorded during fit().""" - check_is_fitted(self, 'booster_') - return self.booster_.evals_result_ - - @property - def best_iteration_(self) -> int: - """Best round index (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_iteration_ - - @property - def best_score_(self) -> float: - """Best monitored metric value (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_score_ - - # score() inherited from RegressorMixin (R² score) - - -class OpenBoostGAMClassifier(BaseEstimator, ClassifierMixin): - """GAM Classifier (binary) with sklearn-compatible interface. - - Wraps :class:`OpenBoostGAM` with ``loss='logloss'`` for use with - GridSearchCV, Pipeline, etc. Supports binary classification only - (the underlying GAM predicts a single sigmoid-linked score). - - Parameters - ---------- - n_estimators : int, default=1000 - Number of boosting rounds. - learning_rate : float, default=0.01 - Shrinkage factor. - reg_lambda : float, default=1.0 - L2 regularization. - n_bins : int, default=254 - Number of histogram bins. - interactions : int, default=0 - Number of pairwise interaction terms (GA2M-style) learned after - main-effects training. 0 disables interactions. - interaction_rounds : int, optional - Boosting rounds for the interaction stage. None uses n_estimators. - smoothing : float, default=0.0 - Fused-ridge smoothing strength for 1D shape functions (0 = off). - monotone : dict, optional - Mapping of feature index -> +1 (non-decreasing) or -1 - (non-increasing) monotonicity constraint. - early_stopping_rounds : int, optional - Stop training if the validation logloss doesn't improve for this - many rounds. Requires eval_set to be passed to fit(). With multiple - eval sets, the last one is monitored. - verbose : int, default=0 - Logging verbosity. - - Attributes - ---------- - classes_ : ndarray - Unique class labels. - n_classes_ : int - Number of classes (always 2). - n_features_in_ : int - Number of features seen during fit. - booster_ : OpenBoostGAM - The underlying fitted model. - shape_values_ : ndarray - 1D shape function lookup tables from the underlying GAM. - interaction_pairs_ : list of tuple - Selected interaction pairs (ranked), when interactions > 0. - evals_result_ : dict - Per-round eval-set metric history recorded during fit(), e.g. - ``{'eval_0': {'logloss': [...]}}``. Empty when no eval_set was used. - best_iteration_ : int - Iteration with best validation score (if early stopping used). - best_score_ : float - Best validation score achieved (if early stopping used). - - Examples - -------- - >>> from openboost import OpenBoostGAMClassifier - >>> clf = OpenBoostGAMClassifier(n_estimators=200) - >>> clf.fit(X_train, y_train) - >>> proba = clf.predict_proba(X_test) # shape (n_samples, 2) - """ - - def __init__( - self, - n_estimators: int = 1000, - learning_rate: float = 0.01, - reg_lambda: float = 1.0, - n_bins: int = 254, - interactions: int = 0, - interaction_rounds: int | None = None, - smoothing: float = 0.0, - monotone: dict[int, int] | None = None, - early_stopping_rounds: int | None = None, - verbose: int = 0, - ) -> None: - self.n_estimators = n_estimators - self.learning_rate = learning_rate - self.reg_lambda = reg_lambda - self.n_bins = n_bins - self.interactions = interactions - self.interaction_rounds = interaction_rounds - self.smoothing = smoothing - self.monotone = monotone - self.early_stopping_rounds = early_stopping_rounds - self.verbose = verbose - - def fit( - self, - X: NDArray, - y: NDArray, - eval_set: list[tuple[NDArray, NDArray]] | None = None, - callbacks: list | None = None, - ) -> OpenBoostGAMClassifier: - """Fit the GAM classifier. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Training features. - y : array-like of shape (n_samples,) - Target class labels (exactly 2 classes). - eval_set : list of (X, y) tuples, optional - Validation set(s) scored with logloss every round; history is - stored in ``evals_result_``. The LAST eval set is monitored by - callbacks / early stopping. Labels are encoded with the - training label encoder. - callbacks : list of Callback, optional - Callbacks forwarded to the underlying booster's fit(). - - Returns - ------- - self : OpenBoostGAMClassifier - Fitted estimator. - """ - _check_sklearn() - X, y = check_X_y(X, y, dtype=np.float32) - self.n_features_in_ = X.shape[1] - - # Encode labels - self._label_encoder = LabelEncoder() - y_encoded = self._label_encoder.fit_transform(y) - self.classes_ = self._label_encoder.classes_ - self.n_classes_ = len(self.classes_) - if self.n_classes_ != 2: - raise ValueError( - "OpenBoostGAMClassifier supports binary classification only; " - f"got {self.n_classes_} class(es). Use OpenBoostClassifier " - "for multi-class problems." - ) - - # Transform eval_set labels if provided (accept a single bare tuple) - if eval_set is not None: - if isinstance(eval_set, tuple): - eval_set = [eval_set] - eval_set = [ - (X_val, self._label_encoder.transform(y_val).astype(np.float32)) - for X_val, y_val in eval_set - ] - - self.booster_ = OpenBoostGAM( - n_trees=self.n_estimators, - learning_rate=self.learning_rate, - reg_lambda=self.reg_lambda, - loss='logloss', - n_bins=self.n_bins, - interactions=self.interactions, - interaction_rounds=self.interaction_rounds, - smoothing=self.smoothing, - monotone=self.monotone, - ) - - all_callbacks = list(callbacks) if callbacks else [] - if self.early_stopping_rounds is not None and eval_set is not None: - all_callbacks.append(EarlyStopping( - patience=self.early_stopping_rounds, - restore_best=True, - verbose=self.verbose > 0, - )) - - self.booster_.fit( - X, y_encoded.astype(np.float32), - callbacks=all_callbacks if all_callbacks else None, - eval_set=eval_set, - ) - return self - - def predict(self, X: NDArray) -> NDArray: - """Predict class labels. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - - Returns - ------- - y_pred : ndarray of shape (n_samples,) - Predicted class labels. - """ - proba = self.predict_proba(X) - indices = np.argmax(proba, axis=1) - return self.classes_[indices] - - def predict_proba(self, X: NDArray) -> NDArray: - """Predict class probabilities. - - Parameters - ---------- - X : array-like of shape (n_samples, n_features) - Features to predict on. - - Returns - ------- - proba : ndarray of shape (n_samples, 2) - Class probabilities. - """ - _check_sklearn() - check_is_fitted(self, 'booster_') - X = check_array(X, dtype=np.float32) - return self.booster_.predict_proba(X) - - @property - def shape_values_(self) -> NDArray: - """Shape function values from the underlying GAM.""" - check_is_fitted(self, 'booster_') - return self.booster_.shape_values_ - - @property - def interaction_pairs_(self) -> list[tuple[int, int]]: - """Selected interaction pairs (ranked) from the underlying GAM.""" - check_is_fitted(self, 'booster_') - return self.booster_.interaction_pairs_ - - @property - def evals_result_(self) -> dict[str, dict[str, list[float]]]: - """Per-round eval-set metric history recorded during fit().""" - check_is_fitted(self, 'booster_') - return self.booster_.evals_result_ - - @property - def best_iteration_(self) -> int: - """Best round index (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_iteration_ - - @property - def best_score_(self) -> float: - """Best monitored metric value (set when early stopping is active).""" - check_is_fitted(self, 'booster_') - return self.booster_.best_score_ - - # score() is inherited from ClassifierMixin (accuracy) diff --git a/src/openboost/_models/_survival.py b/src/openboost/_models/_survival.py deleted file mode 100644 index ca29c81..0000000 --- a/src/openboost/_models/_survival.py +++ /dev/null @@ -1,184 +0,0 @@ -"""WeibullAFT: boosted Weibull survival regression with censoring. - -Both the scale ``lambda(z)`` and the shape ``k(z)`` are boosting ensembles over -the features ``z`` and trained on a right-censored negative log-likelihood. -XGBoost's ``survival:aft`` learns only the location and holds the distribution -scale as a single global hyperparameter, so it cannot vary the Weibull shape -with covariates; NaturalBoost's built-in distributions have no censored -likelihood at all. WeibullAFT expresses both. -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Any - -import numpy as np -from numpy.typing import NDArray - -from .._array import BinnedArray -from .._callbacks import Callback -from .._core._growth import TreeStructure -from .._objectives import WeibullAFTObjective -from .._persistence import PersistenceMixin -from .._trainer import TrainerConfig, fit_boosting, predict_raw -from .._validation import validate_1d - - -@dataclass -class WeibullAFT(PersistenceMixin): - """Boosted Weibull accelerated-failure-time model with right censoring. - - Args: - damp: Levenberg-Marquardt damping on the per-sample 2x2 information. - - Tree knobs (``n_trees``, ``max_depth``, ``learning_rate``, ...) match - ``GradientBoosting``. - - Example: - ```python - m = WeibullAFT(n_trees=300, max_depth=3, learning_rate=0.1) - m.fit(Z, time, event=observed) # event: 1 seen, 0 censored - params = m.predict_params(Z) # per-sample {scale, shape} - t_hat = m.predict(Z) # predicted median time - s = m.predict_survival(Z, t=5.0) # S(5.0 | z) per sample - ``` - """ - - damp: float = 1.0 - n_trees: int = 100 - max_depth: int = 3 - learning_rate: float = 0.1 - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 - subsample: float = 1.0 - colsample_bytree: float = 1.0 - n_bins: int = 254 - - trees_: dict[str, list[TreeStructure]] = field( - default_factory=dict, init=False, repr=False - ) - evals_result_: dict[str, dict[str, list[float]]] = field( - default_factory=dict, init=False, repr=False - ) - X_binned_: BinnedArray | None = field(default=None, init=False, repr=False) - _base_scores: dict[str, float] = field(default_factory=dict, init=False, repr=False) - n_features_in_: int = field(default=0, init=False, repr=False) - _objective: WeibullAFTObjective | None = field( - default=None, init=False, repr=False - ) - - def _make_objective(self) -> WeibullAFTObjective: - return WeibullAFTObjective(damp=self.damp) - - def fit( - self, - X: NDArray, - y: NDArray, - event: NDArray | None = None, - sample_weight: NDArray | None = None, - callbacks: list[Callback] | None = None, - eval_set: list[tuple] | None = None, - early_stopping_rounds: int | None = None, - ) -> WeibullAFT: - """Fit ``lambda(z)``, ``k(z)`` from ``(X, time, event)``. - - ``y`` is the observed time (>0). ``event`` is 1 for an observed event - and 0 for right-censored (default: all observed). ``eval_set`` entries - are ``(X_val, y_val, event_val)``. - """ - y = np.asarray(y, dtype=np.float64).ravel() - if np.any(y <= 0): - raise ValueError("WeibullAFT requires strictly positive times y.") - ev = None if event is None else validate_1d(event, len(y), "event") - self._objective = self._make_objective() - - eval_sets: list[dict[str, Any]] | None = None - if eval_set is not None: - if ( - isinstance(eval_set, tuple) - and len(eval_set) in (2, 3) - and not isinstance(eval_set[0], tuple) - ): - eval_set = [eval_set] - eval_sets = [] - for item in eval_set: - if not (isinstance(item, tuple) and len(item) in (2, 3)): - raise ValueError( - "WeibullAFT eval_set entries must be " - "(X_val, y_val[, event_val])." - ) - X_e, y_e = item[0], np.asarray(item[1], dtype=np.float64).ravel() - ev_e = (validate_1d(item[2], len(y_e), "event") - if len(item) == 3 else None) - eval_sets.append( - {"X": X_e, "y": y_e, "extra": {"event": ev_e}} - ) - - fit_boosting( - self, - self._objective, - X, - y, - config=TrainerConfig( - n_trees=self.n_trees, - max_depth=self.max_depth, - learning_rate=self.learning_rate, - min_child_weight=self.min_child_weight, - reg_lambda=self.reg_lambda, - reg_alpha=self.reg_alpha, - subsample=self.subsample, - colsample_bytree=self.colsample_bytree, - n_bins=self.n_bins, - ), - sample_weight=sample_weight, - extra={"event": ev}, - callbacks=callbacks, - early_stopping_rounds=early_stopping_rounds, - eval_sets=eval_sets, - eval_metric_name="nll", - ) - return self - - def predict_params(self, X: NDArray | BinnedArray) -> dict[str, NDArray]: - """Per-sample constrained Weibull parameters ``{scale, shape}``.""" - if self._objective is None: - self._objective = self._make_objective() - return self._objective.constrain(predict_raw(self, X)) - - def predict_quantile(self, X: NDArray | BinnedArray, q: float = 0.5) -> NDArray: - """Predict the time ``t`` at which ``P(T <= t) = q`` for each row.""" - if not 0.0 < q < 1.0: - raise ValueError("q must be in (0, 1).") - params = self.predict_params(X) - lam, k = params["scale"], params["shape"] - return lam * (-np.log(1.0 - q)) ** (1.0 / k) - - def predict_median(self, X: NDArray | BinnedArray) -> NDArray: - return self.predict_quantile(X, 0.5) - - def predict(self, X: NDArray | BinnedArray) -> NDArray: - """Point prediction = predicted median survival time.""" - return self.predict_median(X) - - def predict_survival(self, X: NDArray | BinnedArray, t: float | NDArray) -> NDArray: - """Survival probability ``S(t | z) = exp(-(t / lambda)^k)`` per row.""" - params = self.predict_params(X) - lam, k = params["scale"], params["shape"] - t = np.asarray(t, dtype=np.float64) - return np.exp(-((t / lam) ** k)) - - def nll( - self, - X: NDArray | BinnedArray, - y: NDArray, - event: NDArray | None = None, - ) -> float: - """Mean censored negative log-likelihood on ``(X, y, event)``.""" - if self._objective is None: - self._objective = self._make_objective() - y = np.asarray(y, dtype=np.float64).ravel() - ev = None if event is None else validate_1d(event, len(y), "event") - raw = predict_raw(self, X) - return self._objective.loss_value(raw, y, None, {"event": ev}) diff --git a/src/openboost/_objectives.py b/src/openboost/_objectives.py deleted file mode 100644 index 1ee716b..0000000 --- a/src/openboost/_objectives.py +++ /dev/null @@ -1,637 +0,0 @@ -"""Objectives for the unified boosting trainer. - -An objective turns current raw scores F (one channel per boosted parameter) -into per-sample step directions (grad, hess) that ``fit_tree`` consumes. - -Two first-class implementations: - -- ``DistributionObjective`` — NLL / natural-gradient path used by - DistributionalGBDT and NaturalBoost. -- ``FormulaObjective`` — user formula ``f(theta, x)`` with damped generalized - Gauss-Newton preconditioning (the FormulaBoost path). -""" - -from __future__ import annotations - -from collections.abc import Callable -from typing import Any, Protocol - -import numpy as np -from numpy.typing import NDArray - -from ._distributions import Distribution - -RawScores = dict[str, NDArray] -GradHess = dict[str, tuple[NDArray, NDArray]] - - -class Objective(Protocol): - """Internal protocol consumed by ``fit_boosting``.""" - - channel_names: list[str] - - def init_raw( - self, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> dict[str, float]: - """Return per-channel base scores in raw (unconstrained) space.""" - ... - - def step( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> GradHess: - """Per-channel (gradient, hessian) w.r.t. raw scores.""" - ... - - def loss_value( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> float: - """Scalar loss (lower is better) for train reporting / default eval.""" - ... - - def constrain( - self, - raw: RawScores, - extra: dict[str, Any] | None = None, - ) -> dict[str, NDArray]: - """Map raw scores to constrained parameters.""" - ... - - -def _is_device(arr) -> bool: - return hasattr(arr, "__cuda_array_interface__") - - -def _apply_sample_weight(grads: GradHess, sample_weight: NDArray | None) -> GradHess: - if sample_weight is None: - return grads - w = sample_weight.astype(np.float32, copy=False) - return { - name: ((g * w).astype(np.float32), (h * w).astype(np.float32)) - for name, (g, h) in grads.items() - } - - -# ============================================================================= -# Distribution objective (NaturalBoost / DistributionalGBDT) -# ============================================================================= - - -class DistributionObjective: - """Boost each distribution parameter from an NLL (optionally natural).""" - - def __init__( - self, - distribution: Distribution, - *, - natural: bool = False, - exposure_param: str | None = None, - exposure_sign: float = 0.0, - ): - self.distribution = distribution - self.natural = natural - self.exposure_param = exposure_param - self.exposure_sign = exposure_sign - self.channel_names = list(distribution.param_names) - self._device_kernels_ok = True - - @property - def device_capable(self) -> bool: - """True when grad/hess can be computed on device-resident raw scores.""" - return type(self.distribution).__name__ in ("Normal", "Poisson") - - @property - def unit_hessian(self) -> bool: - return bool(self.natural) - - def init_raw( - self, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> dict[str, float]: - return {k: float(v) for k, v in self.distribution.init_params(y).items()} - - def constrain( - self, - raw: RawScores, - extra: dict[str, Any] | None = None, - ) -> dict[str, NDArray]: - log_offset = None if extra is None else extra.get("log_offset") - params = {} - for name in self.channel_names: - score = raw[name] - if log_offset is not None and name == self.exposure_param: - score = score + log_offset - params[name] = self.distribution.link(name, score) - return params - - def step( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> GradHess: - first = next(iter(raw.values())) - if ( - self.device_capable - and self._device_kernels_ok - and _is_device(first) - and (extra is None or extra.get("log_offset") is None) - ): - return self._step_device(raw, y, sample_weight) - params = self.constrain(raw, extra) - if self.natural: - grads = self.distribution.natural_gradient(y, params) - else: - grads = self.distribution.nll_gradient(y, params) - return _apply_sample_weight(grads, sample_weight) - - def _step_device( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None, - ) -> GradHess: - try: - from ._backends._cuda import normal_step_gpu, poisson_step_gpu, scale_gh_gpu - - name = type(self.distribution).__name__ - if name == "Normal": - g_loc, h_loc, g_scale, h_scale = normal_step_gpu( - raw["loc"], raw["scale"], y, natural=self.natural - ) - grads: GradHess = {"loc": (g_loc, h_loc), "scale": (g_scale, h_scale)} - elif name == "Poisson": - g, h = poisson_step_gpu(raw["rate"], y, natural=self.natural) - grads = {"rate": (g, h)} - else: # pragma: no cover - raise RuntimeError(f"No device step for {name}") - if sample_weight is not None: - for g, h in grads.values(): - scale_gh_gpu(g, h, sample_weight) - return grads - except Exception: - self._device_kernels_ok = False - # Kernel compile can fail on some numpy/numba-cuda combos; - # fall back to the host path and let the trainer upload grads. - raw_host = { - k: (v.copy_to_host() if hasattr(v, "copy_to_host") else np.asarray(v)) - for k, v in raw.items() - } - y_host = y.copy_to_host() if hasattr(y, "copy_to_host") else np.asarray(y) - sw_host = None - if sample_weight is not None: - sw_host = ( - sample_weight.copy_to_host() - if hasattr(sample_weight, "copy_to_host") - else np.asarray(sample_weight) - ) - params = self.constrain(raw_host) - if self.natural: - grads = self.distribution.natural_gradient(y_host, params) - else: - grads = self.distribution.nll_gradient(y_host, params) - return _apply_sample_weight(grads, sw_host) - - def loss_value( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> float: - params = self.constrain(raw, extra) - nll = self.distribution.nll(y, params) - return float(np.average(nll, weights=sample_weight)) - - -# ============================================================================= -# Formula objective (FormulaBoost) -# ============================================================================= - -# link(raw) -> constrained; link_prime(raw) -> d(constrained)/d(raw) -_LINKS: dict[str, tuple[Callable[[NDArray], NDArray], Callable[[NDArray], NDArray]]] = { - "identity": (lambda r: r, lambda r: np.ones_like(r, dtype=np.float64)), - "log": (np.exp, np.exp), - "softplus": ( - lambda r: np.logaddexp(0.0, r), - lambda r: 1.0 / (1.0 + np.exp(-r)), - ), - "sigmoid": ( - lambda r: 1.0 / (1.0 + np.exp(-r)), - lambda r: (s := 1.0 / (1.0 + np.exp(-r))) * (1.0 - s), - ), -} - - -def _link_pair(name: str): - if name not in _LINKS: - raise ValueError( - f"Unknown link '{name}'. Available: {', '.join(sorted(_LINKS))}." - ) - return _LINKS[name] - - -def _formula_and_jac( - formula: Callable, - theta: list[NDArray], - x: NDArray, - eps: float = 1e-5, -) -> tuple[NDArray, NDArray]: - """Evaluate ``formula(theta, x)`` and a forward-difference Jacobian. - - Returns ``(f, J)`` with ``f`` shape ``(n,)`` and ``J`` shape ``(n, K)`` - where ``J[:, k] = df / d theta_k``. - """ - f0 = np.asarray(formula(tuple(theta), x), dtype=np.float64).ravel() - k = len(theta) - jac = np.empty((f0.shape[0], k), dtype=np.float64) - for j in range(k): - bumped = list(theta) - bumped[j] = theta[j] + eps - f1 = np.asarray(formula(tuple(bumped), x), dtype=np.float64).ravel() - jac[:, j] = (f1 - f0) / eps - return f0, jac - - -def _ggn_step( - residual: NDArray, - jac_raw: NDArray, - precond: str, - damp: float, -) -> NDArray: - """Per-sample GGN step directions, shape ``(n, K)``. - - MSE / 0.5*(f-y)^2 => g = residual * J, G = J^T J (per sample). - """ - g = residual[:, None] * jac_raw # (n, K) - if precond == "plain": - return g - if precond == "diag": - return g / (jac_raw * jac_raw + damp) - if precond != "full": - raise ValueError( - f"Unknown precond '{precond}'. Use 'plain', 'diag', or 'full'." - ) - - n, k = jac_raw.shape - if k == 1: - return g / (jac_raw * jac_raw + damp) - if k == 2: - g00 = jac_raw[:, 0] * jac_raw[:, 0] + damp - g11 = jac_raw[:, 1] * jac_raw[:, 1] + damp - g01 = jac_raw[:, 0] * jac_raw[:, 1] - det = g00 * g11 - g01 * g01 - det = np.where(np.abs(det) < 1e-12, 1e-12, det) - d0 = (g11 * g[:, 0] - g01 * g[:, 1]) / det - d1 = (g00 * g[:, 1] - g01 * g[:, 0]) / det - return np.stack([d0, d1], axis=1) - - # General K: batched (J J^T + damp I)^{-1} g via small dense solves - eye = np.eye(k, dtype=np.float64) - out = np.empty_like(g) - for i in range(n): - ji = jac_raw[i] - gmat = np.outer(ji, ji) + damp * eye - try: - out[i] = np.linalg.solve(gmat, g[i]) - except np.linalg.LinAlgError: - out[i] = np.linalg.solve(gmat + 1e-6 * eye, g[i]) - return out - - -def _fit_global_raw( - formula: Callable, - links: list[str], - x: NDArray, - y: NDArray, - sample_weight: NDArray | None, -) -> np.ndarray: - """Fit a single global raw-parameter vector by L-BFGS-B.""" - from scipy.optimize import minimize - - k = len(links) - x = np.asarray(x, dtype=np.float64).ravel() - y = np.asarray(y, dtype=np.float64).ravel() - - v0 = np.zeros(k, dtype=np.float64) - if links[0] == "log" and np.all(y > 0): - v0[0] = float(np.log(np.average(y, weights=sample_weight))) - - def packed_loss(v: np.ndarray) -> float: - theta = [] - for j, name in enumerate(links): - fn, _ = _link_pair(name) - raw_j = np.full_like(x, v[j]) - theta.append(fn(raw_j)) - pred = np.asarray(formula(tuple(theta), x), dtype=np.float64).ravel() - if not np.all(np.isfinite(pred)): - return 1e12 - err = 0.5 * (pred - y) ** 2 - return float(np.average(err, weights=sample_weight)) - - result = minimize(packed_loss, v0, method="L-BFGS-B") - return result.x if result.success else v0 - - -class FormulaObjective: - """Boost the parameters of a user formula ``f(theta, x)``. - - ``formula(theta, x)`` receives ``theta`` as a tuple of K arrays (constrained - space) and ``x`` as the structural input (e.g. spend). Features ``Z`` that - the trees split on never enter the formula — they only determine - ``theta(Z)``. - """ - - def __init__( - self, - formula: Callable, - n_params: int, - links: tuple[str, ...] | list[str], - *, - loss: str = "mse", - precond: str = "full", - damp: float = 1.0, - param_names: tuple[str, ...] | list[str] | None = None, - fd_eps: float = 1e-5, - ): - if loss != "mse": - raise ValueError("FormulaObjective currently supports loss='mse' only.") - if len(links) != n_params: - raise ValueError( - f"links has length {len(links)}, expected n_params={n_params}." - ) - for name in links: - _link_pair(name) - if precond not in ("plain", "diag", "full"): - raise ValueError( - f"Unknown precond '{precond}'. Use 'plain', 'diag', or 'full'." - ) - - self.formula = formula - self.n_params = n_params - self.links = list(links) - self.loss = loss - self.precond = precond - self.damp = float(damp) - self.fd_eps = float(fd_eps) - if param_names is None: - self.channel_names = [f"theta_{j}" for j in range(n_params)] - else: - if len(param_names) != n_params: - raise ValueError("param_names length must equal n_params.") - self.channel_names = list(param_names) - - @property - def device_capable(self) -> bool: - # Formula + GGN stay on host; the trainer still builds trees on GPU. - return False - - @property - def unit_hessian(self) -> bool: - return True - - def _require_x(self, extra: dict[str, Any] | None) -> NDArray: - if extra is None or extra.get("model_input") is None: - raise ValueError( - "FormulaObjective requires extra['model_input'] " - "(the structural input x that enters the formula)." - ) - return np.asarray(extra["model_input"], dtype=np.float64).ravel() - - def init_raw( - self, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> dict[str, float]: - x = self._require_x(extra) - v = _fit_global_raw(self.formula, self.links, x, y, sample_weight) - return {name: float(v[j]) for j, name in enumerate(self.channel_names)} - - def constrain( - self, - raw: RawScores, - extra: dict[str, Any] | None = None, - ) -> dict[str, NDArray]: - params = {} - for name, link_name in zip(self.channel_names, self.links, strict=True): - fn, _ = _link_pair(link_name) - params[name] = fn(np.asarray(raw[name], dtype=np.float64)) - return params - - def _predict_f(self, raw: RawScores, extra: dict[str, Any] | None) -> NDArray: - x = self._require_x(extra) - params = self.constrain(raw, extra) - theta = [params[name] for name in self.channel_names] - return np.asarray(self.formula(tuple(theta), x), dtype=np.float64).ravel() - - def step( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> GradHess: - x = self._require_x(extra) - y = np.asarray(y, dtype=np.float64).ravel() - theta = [] - link_prime = [] - for name, link_name in zip(self.channel_names, self.links, strict=True): - fn, dfn = _link_pair(link_name) - r = np.asarray(raw[name], dtype=np.float64).ravel() - theta.append(fn(r)) - link_prime.append(dfn(r)) - - f, jac_theta = _formula_and_jac(self.formula, theta, x, eps=self.fd_eps) - jac_raw = jac_theta * np.stack(link_prime, axis=1) - residual = f - y - direction = _ggn_step(residual, jac_raw, self.precond, self.damp) - - n = y.shape[0] - ones = np.ones(n, dtype=np.float32) - grads: GradHess = {} - for j, name in enumerate(self.channel_names): - grads[name] = (direction[:, j].astype(np.float32), ones.copy()) - return _apply_sample_weight(grads, sample_weight) - - def loss_value( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> float: - pred = self._predict_f(raw, extra) - y = np.asarray(y, dtype=np.float64).ravel() - return float(np.average(0.5 * (pred - y) ** 2, weights=sample_weight)) - - -# ============================================================================= -# Weibull AFT objective (survival / censored regression) -# ============================================================================= - -# NGBoost's built-in families have no censored likelihood, and XGBoost's -# survival:aft objective learns only the location while holding the -# distribution scale (shape) as a single global hyperparameter. WeibullAFT -# boosts BOTH the scale lambda(z) and the shape k(z) surfaces from a censored -# NLL, which is the capability neither can express. -# -# Parameterization (both channels live in log space; that is the raw score): -# u = raw["scale"] = log(lambda), lambda = exp(u) -# w = raw["shape"] = log(k), k = exp(w) -# For time t with event indicator delta in {0, 1} (1 = observed, 0 = right -# censored), with m = log(t) - u and z = (t/lambda)^k = exp(k * m): -# NLL = -delta * (w - u + (k - 1) * m) + z -# (i.e. -delta * log hazard + cumulative hazard). - - -class WeibullAFTObjective: - """Boost Weibull scale ``lambda(z)`` and shape ``k(z)`` under a censored NLL. - - ``extra['event']`` is the event indicator (1 observed, 0 right-censored); - defaults to all-observed. Preconditioning is a damped, PD-safeguarded - per-sample 2x2 observed-information solve (Newton-natural step), sharing - the trainer's unit-hessian / GPU-tree path with FormulaBoost. - """ - - _W_CLIP = 12.0 # clip log-shape to keep k = exp(w) finite - _Z_CLIP = 60.0 # clip k*m before exp - _EULER = 0.5772156649015329 - # Expected Fisher info of the log-shape channel for an observed Weibull - # event (constant in the log parameterization): (1-gamma)^2 + pi^2/6. - _I_WW = (1.0 - _EULER) ** 2 + (np.pi ** 2) / 6.0 - - def __init__(self, *, damp: float = 1.0): - self.channel_names = ["scale", "shape"] - self.damp = float(damp) - - @property - def device_capable(self) -> bool: - return False - - @property - def unit_hessian(self) -> bool: - return True - - def _event(self, y: NDArray, extra: dict[str, Any] | None) -> NDArray: - n = len(y) - if extra is None or extra.get("event") is None: - return np.ones(n, dtype=np.float64) - e = np.asarray(extra["event"], dtype=np.float64).ravel() - if e.shape[0] != n: - raise ValueError( - f"event has length {e.shape[0]}, expected {n} (matching y)." - ) - return e - - def _pieces(self, u, w, t): - """Shared intermediates for grad/hess/nll.""" - u = np.asarray(u, dtype=np.float64).ravel() - w = np.clip(np.asarray(w, dtype=np.float64).ravel(), -self._W_CLIP, self._W_CLIP) - k = np.exp(w) - m = np.log(t) - u - z = np.exp(np.clip(k * m, -self._Z_CLIP, self._Z_CLIP)) - return k, m, z - - def init_raw( - self, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> dict[str, float]: - from scipy.optimize import minimize - - t = np.asarray(y, dtype=np.float64).ravel() - if np.any(t <= 0): - raise ValueError("WeibullAFT requires strictly positive times y.") - delta = self._event(t, extra) - - def mean_nll(v): - u, w = np.full_like(t, v[0]), np.full_like(t, v[1]) - k, m, z = self._pieces(u, w, t) - nll = -delta * (w - u + (k - 1.0) * m) + z - if not np.all(np.isfinite(nll)): - return 1e12 - return float(np.average(nll, weights=sample_weight)) - - v0 = np.array([float(np.log(np.median(t))), 0.0]) - res = minimize(mean_nll, v0, method="Nelder-Mead", - options={"xatol": 1e-4, "fatol": 1e-6, "maxiter": 400}) - v = res.x if res.success else v0 - return {"scale": float(v[0]), "shape": float(v[1])} - - def constrain( - self, - raw: RawScores, - extra: dict[str, Any] | None = None, - ) -> dict[str, NDArray]: - u = np.asarray(raw["scale"], dtype=np.float64).ravel() - w = np.clip(np.asarray(raw["shape"], dtype=np.float64).ravel(), - -self._W_CLIP, self._W_CLIP) - return {"scale": np.exp(u), "shape": np.exp(w)} - - def step( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> GradHess: - t = np.asarray(y, dtype=np.float64).ravel() - delta = self._event(t, extra) - u = np.asarray(raw["scale"], dtype=np.float64).ravel() - w = np.asarray(raw["shape"], dtype=np.float64).ravel() - k, m, z = self._pieces(u, w, t) - - # Gradient of the censored NLL w.r.t. raw (u = log-scale, w = log-shape). - g_u = k * (delta - z) - g_w = -delta * (1.0 + m * k) + k * m * z - - # Damped natural gradient: precondition with the EXPECTED Fisher - # information (not the observed Hessian, whose scale term k^2 z blows - # up when lambda is wrong and freezes the update). In the log - # parameterization the per-observed-event Fisher is - # [[k^2, -k(1-gamma)], [-k(1-gamma), (1-gamma)^2 + pi^2/6]]. - off = -k * (1.0 - self._EULER) - a = k * k + self.damp - c = self._I_WW + self.damp - b = off - det = a * c - b * b - - d_u = (c * g_u - b * g_w) / det - d_w = (a * g_w - b * g_u) / det - - ones = np.ones(len(t), dtype=np.float32) - grads: GradHess = { - "scale": (d_u.astype(np.float32), ones.copy()), - "shape": (d_w.astype(np.float32), ones.copy()), - } - return _apply_sample_weight(grads, sample_weight) - - def loss_value( - self, - raw: RawScores, - y: NDArray, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - ) -> float: - t = np.asarray(y, dtype=np.float64).ravel() - delta = self._event(t, extra) - u = np.asarray(raw["scale"], dtype=np.float64).ravel() - w = np.asarray(raw["shape"], dtype=np.float64).ravel() - k, m, z = self._pieces(u, w, t) - nll = -delta * (w - u + (k - 1.0) * m) + z - return float(np.average(nll, weights=sample_weight)) diff --git a/src/openboost/_persistence.py b/src/openboost/_persistence.py deleted file mode 100644 index 0287cd9..0000000 --- a/src/openboost/_persistence.py +++ /dev/null @@ -1,572 +0,0 @@ -"""Model persistence utilities for OpenBoost. - -Phase 20.1: Model Persistence - -Provides save/load functionality for all OpenBoost models using joblib. -""" - -from __future__ import annotations - -from pathlib import Path -from typing import TYPE_CHECKING, Any, TypeVar - -import numpy as np - -if TYPE_CHECKING: - from ._core._growth import TreeStructure - -T = TypeVar("T", bound="PersistenceMixin") - - -def _to_numpy(arr: Any) -> np.ndarray | None: - """Convert array to numpy, handling GPU arrays. - - Args: - arr: Array (numpy, cuda device array, or None) - - Returns: - numpy array or None - """ - if arr is None: - return None - - # Handle numba cuda arrays - if hasattr(arr, "copy_to_host"): - return arr.copy_to_host() - - # Handle cupy arrays - if hasattr(arr, "get"): - return arr.get() - - # Already numpy or compatible - return np.asarray(arr) - - -def _tree_to_dict(tree: TreeStructure) -> dict[str, Any]: - """Convert TreeStructure to a serializable dictionary. - - Args: - tree: TreeStructure instance - - Returns: - Dictionary with tree data - """ - from ._core._growth import ScalarLeaves, VectorLeaves - - data = { - "features": _to_numpy(tree.features), - "thresholds": _to_numpy(tree.thresholds), - "left_children": _to_numpy(tree.left_children), - "right_children": _to_numpy(tree.right_children), - "n_nodes": tree.n_nodes, - "depth": tree.depth, - "n_features": tree.n_features, - "is_symmetric": tree.is_symmetric, - } - - # Handle leaf values (can be array or LeafValues subclass) - # Note: Check specific subclasses first since numpy arrays match the - # LeafValues Protocol (it's runtime_checkable) - if isinstance(tree.values, ScalarLeaves): - data["values"] = _to_numpy(tree.values.values) - data["values_type"] = "scalar" - elif isinstance(tree.values, VectorLeaves): - data["values"] = _to_numpy(tree.values.values) - data["values_type"] = "vector" - else: - # Regular numpy array (or compatible) - data["values"] = _to_numpy(tree.values) - data["values_type"] = "array" - - # Symmetric tree data - if tree.is_symmetric: - data["level_features"] = _to_numpy(tree.level_features) - data["level_thresholds"] = _to_numpy(tree.level_thresholds) - - # Phase 14: Missing value handling - if hasattr(tree, "missing_go_left") and tree.missing_go_left is not None: - data["missing_go_left"] = _to_numpy(tree.missing_go_left) - - # Phase 14.3: Categorical support - if hasattr(tree, "is_categorical") and tree.is_categorical is not None: - data["is_categorical"] = _to_numpy(tree.is_categorical) - if hasattr(tree, "category_masks") and tree.category_masks is not None: - data["category_masks"] = _to_numpy(tree.category_masks) - - return data - - -def _linear_leaf_tree_to_dict(tree) -> dict[str, Any]: - """Convert LinearLeafTree to a serializable dictionary. - - Args: - tree: LinearLeafTree instance - - Returns: - Dictionary with tree data - """ - return { - "tree_structure": _tree_to_dict(tree.tree_structure), - "leaf_weights": _to_numpy(tree.leaf_weights), - "leaf_features": tree.leaf_features, - "leaf_ids": tree.leaf_ids, - "n_features": tree.n_features, - "_type": "LinearLeafTree", - } - - -def _dict_to_linear_leaf_tree(data: dict[str, Any]): - """Reconstruct LinearLeafTree from dictionary. - - Args: - data: Dictionary with tree data - - Returns: - LinearLeafTree instance - """ - from ._models._linear_leaf import LinearLeafTree - - return LinearLeafTree( - tree_structure=_dict_to_tree(data["tree_structure"]), - leaf_weights=data["leaf_weights"], - leaf_features=data["leaf_features"], - leaf_ids=data["leaf_ids"], - n_features=data["n_features"], - ) - - -def _dict_to_tree(data: dict[str, Any]) -> TreeStructure: - """Reconstruct TreeStructure from dictionary. - - Args: - data: Dictionary with tree data - - Returns: - TreeStructure instance - """ - from ._core._growth import ScalarLeaves, TreeStructure, VectorLeaves - - # Handle leaf values based on type - values_type = data.get("values_type", "array") - values_arr = data["values"] - - if values_type == "scalar": - values = ScalarLeaves(values_arr) - elif values_type == "vector": - values = VectorLeaves(values_arr) - else: - values = values_arr - - tree = TreeStructure( - features=data["features"], - thresholds=data["thresholds"], - left_children=data["left_children"], - right_children=data["right_children"], - values=values, - n_nodes=data["n_nodes"], - depth=data["depth"], - n_features=data["n_features"], - is_symmetric=data.get("is_symmetric", False), - level_features=data.get("level_features"), - level_thresholds=data.get("level_thresholds"), - ) - - # Phase 14: Missing value handling - if "missing_go_left" in data: - tree.missing_go_left = data["missing_go_left"] - - # Phase 14.3: Categorical support - if "is_categorical" in data: - tree.is_categorical = data["is_categorical"] - if "category_masks" in data: - tree.category_masks = data["category_masks"] - - return tree - - -class PersistenceMixin: - """Mixin class providing save/load functionality for models. - - Usage: - @dataclass - class MyModel(PersistenceMixin): - n_trees: int = 100 - trees_: list = field(default_factory=list, init=False) - - def _get_persist_attrs(self) -> list[str]: - return ['n_trees', 'trees_'] - """ - - def _get_persist_attrs(self) -> list[str]: - """Return list of attribute names to persist. - - Override in subclass to customize what gets saved. - By default, saves all non-private attributes. - - Returns: - List of attribute names - """ - # Get all attributes from dataclass fields - if hasattr(self, "__dataclass_fields__"): - attrs = list(self.__dataclass_fields__.keys()) - # Also include fitted attributes (sklearn convention: trailing _) - # and other instance attributes not in dataclass fields - for k in vars(self): - if k not in attrs and not k.startswith("_"): - attrs.append(k) - return attrs - # Fallback: all non-private attributes - return [k for k in vars(self) if not k.startswith("_")] - - def _to_state_dict(self) -> dict[str, Any]: - """Convert model to a serializable state dictionary. - - Returns: - Dictionary containing all model state - """ - state = {"__class__": type(self).__name__, "_serialization_version": 1} - - for attr in self._get_persist_attrs(): - value = getattr(self, attr, None) - - # Handle tree dict (distributional models: param_name -> tree list) - if attr == "trees_" and isinstance(value, dict): - state[attr] = { - k: [_tree_to_dict(t) for t in v] for k, v in value.items() - } - state["_trees_type"] = "dict" - # Handle nested tree lists (multiclass: list of tree lists) - elif attr == "trees_" and isinstance(value, list) and value and isinstance(value[0], list): - state[attr] = [[_tree_to_dict(t) for t in trees] for trees in value] - state["_trees_type"] = "nested_list" - # Handle LinearLeafTree lists (check for tree_structure attribute) - elif attr == "trees_" and isinstance(value, list) and value and hasattr(value[0], "tree_structure"): - state[attr] = [_linear_leaf_tree_to_dict(t) for t in value] - state["_trees_type"] = "linear_leaf" - # Handle simple tree lists - elif attr == "trees_" and isinstance(value, list): - state[attr] = [_tree_to_dict(t) for t in value] - state["_trees_type"] = "list" - # Handle BinnedArray (save bin edges for transform) - elif attr == "X_binned_": - if value is not None: - state["_bin_edges"] = value.bin_edges - state["_n_features"] = value.n_features - if hasattr(value, "has_missing"): - state["_has_missing"] = _to_numpy(value.has_missing) - if hasattr(value, "is_categorical"): - state["_is_categorical"] = _to_numpy(value.is_categorical) - if hasattr(value, "category_maps"): - state["_category_maps"] = value.category_maps - if hasattr(value, "n_categories"): - state["_n_categories"] = _to_numpy(value.n_categories) - continue # Don't save the full BinnedArray - # Handle loss function (save name, not function) - elif attr == "_loss_fn": - continue # Skip - will be recreated from loss param - # Skip distribution instance (will be recreated from distribution param) - elif attr == "distribution_": - continue - # Handle arrays - elif hasattr(value, "shape"): - state[attr] = _to_numpy(value) - # Everything else - else: - state[attr] = value - - return state - - def _from_state_dict(self, state: dict[str, Any]) -> None: - """Restore model state from dictionary. - - Args: - state: Dictionary containing model state - """ - import warnings - - _CURRENT_SERIALIZATION_VERSION = 1 - saved_version = state.get("_serialization_version") - if saved_version is None: - warnings.warn( - "Loading a model saved without a serialization version number. " - "The model may have been saved with an older version of OpenBoost.", - UserWarning, - stacklevel=2, - ) - elif saved_version > _CURRENT_SERIALIZATION_VERSION: - warnings.warn( - f"Model was saved with serialization version {saved_version}, " - f"but current version is {_CURRENT_SERIALIZATION_VERSION}. " - "Some features may not load correctly.", - UserWarning, - stacklevel=2, - ) - - trees_type = state.get("_trees_type", "list") - - for attr, value in state.items(): - if attr in ("__class__", "_trees_type"): - continue - - # Handle tree structures based on stored type - if attr == "trees_": - if trees_type == "dict" and isinstance(value, dict): - # Distributional: param_name -> tree list - setattr( - self, - attr, - {k: [_dict_to_tree(d) for d in v] for k, v in value.items()}, - ) - elif trees_type == "nested_list" and isinstance(value, list): - # Multi-class: list of list of tree dicts - setattr( - self, - attr, - [[_dict_to_tree(d) for d in trees] for trees in value], - ) - elif trees_type == "linear_leaf" and isinstance(value, list): - # LinearLeafTree list - setattr( - self, - attr, - [_dict_to_linear_leaf_tree(d) for d in value], - ) - elif isinstance(value, list) and value and isinstance(value[0], dict): - # Single-output: list of tree dicts - setattr(self, attr, [_dict_to_tree(d) for d in value]) - else: - setattr(self, attr, value) - else: - setattr(self, attr, value) - - # Restore bin edges for transform - if "_bin_edges" in state: - import numpy as np - - from ._array import BinnedArray - - # Create a minimal BinnedArray with just bin edges for transform - n_features = state.get("_n_features", len(state["_bin_edges"])) - has_missing = state.get("_has_missing", np.array([], dtype=np.bool_)) - is_categorical = state.get("_is_categorical", np.array([], dtype=np.bool_)) - category_maps = state.get("_category_maps", []) - n_categories = state.get("_n_categories", np.array([], dtype=np.int32)) - - # Create placeholder data (empty, just need structure for transform) - placeholder_data = np.zeros((n_features, 0), dtype=np.uint8) - - self.X_binned_ = BinnedArray( - data=placeholder_data, - bin_edges=state["_bin_edges"], - n_features=n_features, - n_samples=0, # Placeholder - device="cpu", - has_missing=has_missing if isinstance(has_missing, np.ndarray) else np.array(has_missing, dtype=np.bool_), - is_categorical=is_categorical if isinstance(is_categorical, np.ndarray) else np.array(is_categorical, dtype=np.bool_), - category_maps=category_maps, - n_categories=n_categories if isinstance(n_categories, np.ndarray) else np.array(n_categories, dtype=np.int32), - ) - - # Reconstruct _loss_fn from stored loss name/config - if hasattr(self, 'loss') and self.loss is not None: - try: - from openboost._loss import get_loss_function - self._loss_fn = get_loss_function(self.loss) - except Exception: - pass # Will be recreated on next use if needed - - # Reconstruct distribution_ from stored distribution name/config - if hasattr(self, 'distribution') and self.distribution is not None: - try: - from openboost._distributions import get_distribution - self.distribution_ = get_distribution(self.distribution) - except Exception: - pass # Will be recreated on next use if needed - - # Call post-load hook if defined (for recreating derived attributes) - if hasattr(self, "_post_load"): - self._post_load() - - def save(self, path: str | Path) -> None: - """Save model to file. - - Uses joblib for efficient serialization of numpy arrays. - - Args: - path: File path. Recommended extensions: .joblib, .pkl - - Example: - >>> model = ob.GradientBoosting(n_trees=100) - >>> model.fit(X_train, y_train) - >>> model.save('my_model.joblib') - """ - import joblib - - path = Path(path) - state = self._to_state_dict() - joblib.dump(state, path) - - @classmethod - def load(cls: type[T], path: str | Path) -> T: - """Load model from file. - - .. warning:: - This method uses joblib/pickle deserialization which can execute - arbitrary code. Never load models from untrusted or unverified - sources. - - Args: - path: File path to load from - - Returns: - Loaded model instance - - Example: - >>> model = ob.GradientBoosting.load('my_model.joblib') - >>> predictions = model.predict(X_test) - """ - import warnings - - import joblib - - warnings.warn( - "Loading a model with joblib/pickle can execute arbitrary code. " - "Only load models from trusted sources.", - UserWarning, - stacklevel=2, - ) - - path = Path(path) - state = joblib.load(path) - - # Verify class matches - saved_class = state.get("__class__", "") - if saved_class and saved_class != cls.__name__: - raise ValueError( - f"Model was saved as {saved_class}, but loading as {cls.__name__}. " - f"Use {saved_class}.load() instead." - ) - - # Create instance without calling __init__ - model = cls.__new__(cls) - - # Initialize default values from dataclass - if hasattr(cls, "__dataclass_fields__"): - from dataclasses import MISSING - - for name, field_info in cls.__dataclass_fields__.items(): - # Check for default value (not MISSING) - if field_info.default is not MISSING: - setattr(model, name, field_info.default) - # Check for default_factory (not MISSING) - elif field_info.default_factory is not MISSING: - setattr(model, name, field_info.default_factory()) - # No default - leave unset, will be set by _from_state_dict - - # Restore state - model._from_state_dict(state) - - return model - - def __getstate__(self) -> dict[str, Any]: - """Support for pickle serialization.""" - return self._to_state_dict() - - def __setstate__(self, state: dict[str, Any]) -> None: - """Support for pickle deserialization.""" - self._from_state_dict(state) - - -def load(path: str | Path) -> PersistenceMixin: - """Load any OpenBoost model from file, auto-detecting the model class. - - This is a convenience function that reads the saved class name from the - state dict and dispatches to the correct class loader. - - .. warning:: - This method uses joblib/pickle deserialization which can execute - arbitrary code. Never load models from untrusted or unverified sources. - - Args: - path: File path to load from. - - Returns: - Loaded model instance (GradientBoosting, DART, NaturalBoost, etc.) - - Example: - >>> model = ob.load('my_model.joblib') - >>> predictions = model.predict(X_test) - """ - import warnings - - import joblib - - warnings.warn( - "Loading a model with joblib/pickle can execute arbitrary code. " - "Only load models from trusted sources.", - UserWarning, - stacklevel=2, - ) - - path = Path(path) - state = joblib.load(path) - saved_class = state.get("__class__", "") - - # Lazy imports to avoid circular dependencies - from ._models._boosting import GradientBoosting, MultiClassGradientBoosting - from ._models._dart import DART - from ._models._distributional import ( - DistributionalGBDT, - NaturalBoost, - NaturalBoostGamma, - NaturalBoostLogNormal, - NaturalBoostNegBin, - NaturalBoostNormal, - NaturalBoostPoisson, - NaturalBoostStudentT, - NaturalBoostTweedie, - ) - from ._models._gam import OpenBoostGAM - from ._models._linear_leaf import LinearLeafGBDT - - _CLASS_MAP: dict[str, type[PersistenceMixin]] = { - cls.__name__: cls - for cls in [ - GradientBoosting, - MultiClassGradientBoosting, - DART, - OpenBoostGAM, - DistributionalGBDT, - NaturalBoost, - NaturalBoostNormal, - NaturalBoostLogNormal, - NaturalBoostGamma, - NaturalBoostPoisson, - NaturalBoostStudentT, - NaturalBoostTweedie, - NaturalBoostNegBin, - LinearLeafGBDT, - ] - } - - if saved_class not in _CLASS_MAP: - raise ValueError( - f"Unknown model class: {saved_class!r}. " - f"Known classes: {sorted(_CLASS_MAP.keys())}" - ) - - cls = _CLASS_MAP[saved_class] - - # Create instance and restore state (same logic as PersistenceMixin.load - # but without the redundant joblib.load) - model = cls.__new__(cls) - if hasattr(cls, "__dataclass_fields__"): - from dataclasses import MISSING - - for name, field_info in cls.__dataclass_fields__.items(): - if field_info.default is not MISSING: - setattr(model, name, field_info.default) - elif field_info.default_factory is not MISSING: - setattr(model, name, field_info.default_factory()) - model._from_state_dict(state) - return model diff --git a/src/openboost/_profiler.py b/src/openboost/_profiler.py deleted file mode 100644 index 2c4ebd9..0000000 --- a/src/openboost/_profiler.py +++ /dev/null @@ -1,547 +0,0 @@ -"""Profiling callback for OpenBoost training. - -Instruments the training loop to produce structured JSON reports that -break down time by phase (histogram building, split finding, partitioning, -etc.). Designed for self-recursive improvement loops: profile → identify -bottleneck → optimize → re-profile → verify improvement. - -Usage: - # Explicit callback - from openboost import GradientBoosting, ProfilingCallback - profiler = ProfilingCallback(output_dir="logs/") - model = GradientBoosting(n_trees=100) - model.fit(X, y, callbacks=[profiler]) - print(profiler.report_path) - - # Environment variable (zero-code-change) - OPENBOOST_PROFILE=1 uv run python train.py -""" - -from __future__ import annotations - -import json -import os -import platform -import subprocess -import time -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - -from ._callbacks import Callback, TrainingState - -# ============================================================================= -# Phase timer -# ============================================================================= - -class PhaseTimer: - """Accumulates time for a named phase across multiple calls.""" - - __slots__ = ("name", "_use_cuda", "_times", "_start") - - def __init__(self, name: str, use_cuda: bool = False): - self.name = name - self._use_cuda = use_cuda - self._times: list[float] = [] - self._start: float | None = None - - def start(self) -> None: - if self._use_cuda: - from numba import cuda - cuda.synchronize() - self._start = time.perf_counter() - - def stop(self) -> float: - if self._use_cuda: - from numba import cuda - cuda.synchronize() - elapsed = time.perf_counter() - self._start - self._times.append(elapsed) - self._start = None - return elapsed - - @property - def total(self) -> float: - return sum(self._times) - - @property - def count(self) -> int: - return len(self._times) - - @property - def mean(self) -> float: - return self.total / self.count if self._times else 0.0 - - def to_dict(self, total_time: float) -> dict: - return { - "total_s": round(self.total, 6), - "pct": round(100 * self.total / total_time, 2) if total_time > 0 else 0, - "calls": self.count, - "mean_s": round(self.mean, 6), - } - - -# ============================================================================= -# Bottleneck recommendations -# ============================================================================= - -PHASE_RECOMMENDATIONS: dict[str, tuple[str, str]] = { - "histogram_build": ( - "_backends/_cpu.py:build_histogram_cpu, _backends/_cuda.py:_build_histogram_shared_kernel", - "shared-memory tiling, feature batching, reducing n_bins", - ), - "split_find": ( - "_core/_primitives.py:find_node_splits, _core/_split.py:find_best_split", - "GPU parallel scan, vectorized prefix-sum split evaluation", - ), - "partition": ( - "_core/_primitives.py:partition_samples", - "radix-sort-based partitioning, sorted index schemes", - ), - "gradient_compute": ( - "_loss.py loss functions", - "fused GPU kernels, avoiding CPU-GPU copies for custom losses", - ), - "prediction_update": ( - "_models/_boosting.py prediction update loop", - "fusing tree traversal + add, batching prediction updates", - ), - "leaf_values": ( - "_core/_primitives.py:compute_leaf_values", - "GPU reduction kernel, batch leaf computation", - ), - "tree_overhead": ( - "_core/_tree.py:fit_tree, _core/_growth.py:LevelWiseGrowth.grow", - "reduce Python overhead in growth loop, minimize object allocation", - ), - "grad_pred_loss": ( - "_models/_boosting.py training loop (loss_fn, tree predict, loss eval)", - "fuse gradient+prediction, skip loss eval when no callbacks need it", - ), -} - - -# ============================================================================= -# Hardware info -# ============================================================================= - -def _collect_hardware_info() -> dict: - info: dict[str, Any] = { - "cpu": platform.processor() or platform.machine(), - "cpu_cores": os.cpu_count(), - "ram_gb": None, - "gpu": None, - "gpu_memory_gb": None, - } - if platform.system() == "Darwin": - try: - result = subprocess.run( - ["sysctl", "-n", "hw.memsize"], - capture_output=True, text=True, timeout=5, - ) - info["ram_gb"] = round(int(result.stdout.strip()) / (1024**3), 1) - except Exception: - pass - elif platform.system() == "Linux": - try: - with open("/proc/meminfo") as f: - for line in f: - if line.startswith("MemTotal"): - info["ram_gb"] = round(int(line.split()[1]) / (1024**2), 1) - break - except Exception: - pass - try: - from numba import cuda - if cuda.is_available(): - dev = cuda.get_current_device() - info["gpu"] = dev.name.decode() if isinstance(dev.name, bytes) else str(dev.name) - except Exception: - pass - return info - - -def _get_git_sha() -> str | None: - try: - result = subprocess.run( - ["git", "rev-parse", "--short", "HEAD"], - capture_output=True, text=True, timeout=5, - ) - return result.stdout.strip() if result.returncode == 0 else None - except Exception: - return None - - -# ============================================================================= -# Profiling callback -# ============================================================================= - -_PRIMITIVES_TO_WRAP = [ - "build_node_histograms", - "find_node_splits", - "partition_samples", - "compute_leaf_values", -] - -_PHASE_NAMES = { - "build_node_histograms": "histogram_build", - "find_node_splits": "split_find", - "partition_samples": "partition", - "compute_leaf_values": "leaf_values", -} - -# GPU-native profiling phases (must match keys in _cuda._gpu_profile_timers) -_GPU_NATIVE_PHASES = ["histogram_build", "split_find", "partition", "leaf_values"] - -# Modules where fit_tree / fit_tree_gpu_native are imported and need wrapping -_FIT_TREE_MODULES = [ - "openboost._core._tree", - "openboost._models._boosting", -] - - -class ProfilingCallback(Callback): - """Profile training phases and produce structured JSON reports. - - Wraps core primitive functions with timers during training to measure - per-phase time breakdown. Writes a JSON report to output_dir on completion. - - Args: - output_dir: Directory for profile JSON files. Created if missing. - compare_last: If True, compare with the most recent previous profile. - """ - - def __init__(self, output_dir: str = "logs/", compare_last: bool = True): - self.output_dir = Path(output_dir) - self.compare_last = compare_last - - self._timers: dict[str, PhaseTimer] = {} - self._tree_timers: list[dict[str, float]] = [] - self._round_start: float = 0.0 - self._round_phase_snapshot: dict[str, float] = {} - self._train_start: float = 0.0 - self._originals: dict[str, Any] = {} - self._use_cuda: bool = False - - self.report_path: Path | None = None - self.report: dict | None = None - - def _get_timer(self, name: str) -> PhaseTimer: - if name not in self._timers: - self._timers[name] = PhaseTimer(name, use_cuda=self._use_cuda) - return self._timers[name] - - # ----- wrapping / unwrapping ----- - - def _wrap_primitives(self) -> None: - import sys - - import openboost._core._growth as growth_mod - import openboost._core._primitives as prims_mod - - def make_wrapper(orig, tmr): - def wrapper(*args, **kwargs): - tmr.start() - result = orig(*args, **kwargs) - tmr.stop() - return result - return wrapper - - # Wrap the 4 core primitives (used by CPU path and growth strategies) - for func_name in _PRIMITIVES_TO_WRAP: - original = getattr(prims_mod, func_name) - self._originals[("prim", func_name)] = original - phase_name = _PHASE_NAMES[func_name] - timer = self._get_timer(phase_name) - wrapped = make_wrapper(original, timer) - setattr(prims_mod, func_name, wrapped) - if hasattr(growth_mod, func_name): - setattr(growth_mod, func_name, wrapped) - - # Wrap fit_tree to capture total tree-building time (includes orchestration) - fit_tree_timer = self._get_timer("fit_tree") - for mod_name in _FIT_TREE_MODULES: - mod = sys.modules.get(mod_name) - if mod and hasattr(mod, "fit_tree"): - original_ft = mod.fit_tree - self._originals[("fit_tree", mod_name)] = original_ft - mod.fit_tree = make_wrapper(original_ft, fit_tree_timer) - - # Wrap fit_tree_gpu_native — the GPU-native path bypasses shared - # primitives, so we wrap it separately and use _gpu_profile_timers - # inside build_tree_gpu_native for per-phase breakdown. - if self._use_cuda: - self._setup_gpu_native_profiling(make_wrapper) - - def _setup_gpu_native_profiling(self, make_wrapper) -> None: - """Instrument GPU-native tree builder for per-phase profiling.""" - import sys - - import openboost._backends._cuda as cuda_mod - - # Activate per-phase timers inside build_tree_gpu_native - cuda_mod._gpu_profile_timers = {p: 0.0 for p in _GPU_NATIVE_PHASES} - self._originals[("gpu_timers", "_cuda")] = None # sentinel for teardown - - # Wrap fit_tree_gpu_native as fit_tree equivalent - fit_tree_timer = self._get_timer("fit_tree") - for mod_name in _FIT_TREE_MODULES: - mod = sys.modules.get(mod_name) - if mod and hasattr(mod, "fit_tree_gpu_native"): - original = mod.fit_tree_gpu_native - self._originals[("fit_tree_gpu_native", mod_name)] = original - mod.fit_tree_gpu_native = make_wrapper(original, fit_tree_timer) - - def _teardown_gpu_native_profiling(self) -> None: - """Collect GPU-native phase timers and reset hook.""" - import openboost._backends._cuda as cuda_mod - - timers = cuda_mod._gpu_profile_timers - if timers: - # Transfer accumulated times into PhaseTimers. - # Distribute total time across n_trees so call count is correct. - n_trees = self._timers["fit_tree"].count if "fit_tree" in self._timers else 1 - for phase in _GPU_NATIVE_PHASES: - accum = timers.get(phase, 0.0) - if accum > 0: - timer = self._get_timer(phase) - per_tree = accum / n_trees - timer._times.extend([per_tree] * n_trees) - cuda_mod._gpu_profile_timers = None - - def _unwrap_primitives(self) -> None: - import sys - - import openboost._core._growth as growth_mod - import openboost._core._primitives as prims_mod - - # Collect GPU-native timers before unwrapping - if ("gpu_timers", "_cuda") in self._originals: - self._teardown_gpu_native_profiling() - - for key, original in self._originals.items(): - kind, name = key - if kind == "prim": - setattr(prims_mod, name, original) - if hasattr(growth_mod, name): - setattr(growth_mod, name, original) - elif kind == "fit_tree": - mod = sys.modules.get(name) - if mod: - mod.fit_tree = original - elif kind == "fit_tree_gpu_native": - mod = sys.modules.get(name) - if mod: - mod.fit_tree_gpu_native = original - self._originals.clear() - - # ----- callback hooks ----- - - def on_train_begin(self, state: TrainingState) -> None: - from ._backends import is_cuda - self._use_cuda = is_cuda() - self._timers.clear() - self._tree_timers.clear() - self._wrap_primitives() - self._train_start = time.perf_counter() - - def on_round_begin(self, state: TrainingState) -> None: - self._round_start = time.perf_counter() - self._round_phase_snapshot = { - name: timer.total for name, timer in self._timers.items() - } - - def on_round_end(self, state: TrainingState) -> bool: - round_total = time.perf_counter() - self._round_start - tree_entry: dict[str, float] = {"round": state.round_idx, "total_s": round_total} - for name, timer in self._timers.items(): - prev = self._round_phase_snapshot.get(name, 0.0) - tree_entry[f"{name}_s"] = round(timer.total - prev, 6) - # Compute per-tree derived phases - ft = tree_entry.get("fit_tree_s", 0) - prims = sum(tree_entry.get(f"{p}_s", 0) for p in - ("histogram_build", "split_find", "partition", "leaf_values")) - tree_entry["tree_overhead_s"] = round(max(0.0, ft - prims), 6) - tree_entry["grad_pred_loss_s"] = round(max(0.0, round_total - ft), 6) - self._tree_timers.append(tree_entry) - return True - - def on_train_end(self, state: TrainingState) -> None: - total_time = time.perf_counter() - self._train_start - self._unwrap_primitives() - - # Compute derived phases from per-tree data - # round_total = gradient_compute + fit_tree + prediction_update + loss_eval - # fit_tree = primitives + orchestration_overhead - total_round_time = sum(t["total_s"] for t in self._tree_timers) - fit_tree_total = self._timers["fit_tree"].total if "fit_tree" in self._timers else 0 - # Time outside fit_tree but inside rounds = grad compute + pred update + loss eval - outside_tree = max(0.0, total_round_time - fit_tree_total) - # Primitives total - prims_total = sum( - self._timers[p].total for p in ("histogram_build", "split_find", "partition", "leaf_values") - if p in self._timers - ) - # Orchestration = fit_tree - primitives (Python overhead in growth strategies) - orchestration = max(0.0, fit_tree_total - prims_total) - - # Build phases dict (show the most useful breakdown) - phases = {} - for name in ("histogram_build", "split_find", "partition", "leaf_values"): - if name in self._timers: - phases[name] = self._timers[name].to_dict(total_time) - # Add fit_tree orchestration overhead - if orchestration > 0: - n_trees = self._timers["fit_tree"].count if "fit_tree" in self._timers else 0 - phases["tree_overhead"] = { - "total_s": round(orchestration, 6), - "pct": round(100 * orchestration / total_time, 2) if total_time > 0 else 0, - "calls": n_trees, - "mean_s": round(orchestration / n_trees, 6) if n_trees > 0 else 0, - } - # Add outside-tree time (gradient + prediction + loss eval) - if outside_tree > 0: - n_rounds = len(self._tree_timers) - phases["grad_pred_loss"] = { - "total_s": round(outside_tree, 6), - "pct": round(100 * outside_tree / total_time, 2) if total_time > 0 else 0, - "calls": n_rounds, - "mean_s": round(outside_tree / n_rounds, 6) if n_rounds > 0 else 0, - } - # Other: time outside the training loop entirely (setup, teardown) - accounted = fit_tree_total + outside_tree - other_time = max(0.0, total_time - accounted) - if other_time > 0.001: - phases["other"] = { - "total_s": round(other_time, 6), - "pct": round(100 * other_time / total_time, 2) if total_time > 0 else 0, - "calls": None, - "mean_s": None, - } - - # Bottlenecks: top 3 phases by pct (excluding "other") - ranked = sorted( - [(name, data) for name, data in phases.items() if name != "other"], - key=lambda x: x[1]["pct"], - reverse=True, - ) - bottlenecks = [] - for rank, (phase, data) in enumerate(ranked[:3], 1): - target, rec = PHASE_RECOMMENDATIONS.get(phase, ("unknown", "investigate")) - bottlenecks.append({ - "rank": rank, - "phase": phase, - "pct": data["pct"], - "target": target, - "recommendation": rec, - }) - - # Dataset / model info - model = state.model - n_trees_actual = len(getattr(model, "trees_", [])) - dataset_info = { - "n_samples": (model.X_binned_.n_samples - if getattr(model, "X_binned_", None) else None), - "n_features": getattr(model, "n_features_in_", None), - "n_trees": n_trees_actual, - "max_depth": getattr(model, "max_depth", None), - "learning_rate": getattr(model, "learning_rate", None), - "loss": str(getattr(model, "loss", None)), - "backend": "cuda" if self._use_cuda else "cpu", - } - - report = { - "version": "1.0", - "timestamp": datetime.now(timezone.utc).isoformat(), - "git_sha": _get_git_sha(), - "hardware": _collect_hardware_info(), - "dataset": dataset_info, - "total_time_s": round(total_time, 6), - "phases": phases, - "per_tree": self._tree_timers, - "bottlenecks": bottlenecks, - } - - # Comparison with previous run - self.output_dir.mkdir(parents=True, exist_ok=True) - if self.compare_last: - comparison = self._compare_with_previous(report) - if comparison: - report["comparison"] = comparison - - # Write report - ts = datetime.now().strftime("%Y%m%d_%H%M%S") - self.report_path = self.output_dir / f"profile_{ts}.json" - with open(self.report_path, "w") as f: - json.dump(report, f, indent=2) - - self.report = report - - # ----- comparison ----- - - def _compare_with_previous(self, current: dict) -> dict | None: - existing = sorted(self.output_dir.glob("profile_*.json")) - if not existing: - return None - prev_path = existing[-1] - try: - with open(prev_path) as f: - prev = json.load(f) - except (json.JSONDecodeError, OSError): - return None - - prev_total = prev.get("total_time_s", 0) - cur_total = current["total_time_s"] - - comparison: dict[str, Any] = { - "previous_run": str(prev_path), - "delta_total_pct": _pct_delta(prev_total, cur_total), - "phase_deltas": {}, - } - for phase in current["phases"]: - if phase in prev.get("phases", {}): - prev_s = prev["phases"][phase]["total_s"] - cur_s = current["phases"][phase]["total_s"] - comparison["phase_deltas"][phase] = { - "previous_s": prev_s, - "current_s": cur_s, - "delta_pct": _pct_delta(prev_s, cur_s), - } - return comparison - - -def _pct_delta(old: float, new: float) -> float: - if old == 0: - return 0.0 - return round(100 * (new - old) / old, 2) - - -# ============================================================================= -# Summary printer (machine-readable for improvement loops) -# ============================================================================= - -def print_profile_summary(report: dict) -> None: - """Print a machine-readable summary of a profile report.""" - print("=== PROFILE SUMMARY ===") - print(f"TOTAL: {report['total_time_s']:.2f}s") - print(f"BACKEND: {report['dataset'].get('backend', 'unknown')}") - - if report.get("bottlenecks"): - top = report["bottlenecks"][0] - print(f"TOP BOTTLENECK: {top['phase']} ({top['pct']}%)") - print(f"TARGET: {top['target']}") - print(f"RECOMMENDATION: {top['recommendation']}") - - if report.get("comparison"): - comp = report["comparison"] - delta = comp["delta_total_pct"] - sign = "+" if delta > 0 else "" - print(f"DELTA vs PREVIOUS: {sign}{delta}% total") - for phase, pd in comp.get("phase_deltas", {}).items(): - if abs(pd["delta_pct"]) >= 5: - s = "+" if pd["delta_pct"] > 0 else "" - print(f" {phase}: {s}{pd['delta_pct']}%") - else: - print("DELTA vs PREVIOUS: (no previous run)") - - print(f"REPORT: {report.get('_path', 'N/A')}") diff --git a/src/openboost/_sampling.py b/src/openboost/_sampling.py deleted file mode 100644 index 11be689..0000000 --- a/src/openboost/_sampling.py +++ /dev/null @@ -1,568 +0,0 @@ -"""Sampling strategies for large-scale training. - -Phase 17: GOSS (Gradient-based One-Side Sampling) and mini-batch support. - -This module provides sampling strategies to speed up training while -maintaining model quality: -- GOSS: Keep high-gradient samples, subsample low-gradient samples -- Random: Standard random subsampling (baseline) -- MiniBatch: Chunked processing for datasets larger than memory -""" - -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass -from enum import Enum -from typing import TYPE_CHECKING - -import numpy as np - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -class SamplingStrategy(str, Enum): - """Available sampling strategies.""" - RANDOM = "random" - GOSS = "goss" - NONE = "none" - - -@dataclass -class GOSSConfig: - """Configuration for Gradient-based One-Side Sampling (GOSS). - - From LightGBM paper: Keep all samples with large gradients, - subsample those with small gradients. - - Effective sample ratio = top_rate + other_rate * (1 - top_rate) - - Args: - top_rate: Fraction of samples with largest gradient magnitudes to keep. - These are the "important" samples that contribute most to learning. - other_rate: Fraction of remaining samples to randomly sample. - These get upweighted to maintain unbiased gradients. - seed: Random seed for reproducibility. - - Example: - >>> # Keep top 20%, sample 10% of rest = 28% total samples - >>> config = GOSSConfig(top_rate=0.2, other_rate=0.1) - >>> - >>> # More aggressive (faster but less accurate) - >>> config = GOSSConfig(top_rate=0.1, other_rate=0.05) # 14.5% samples - >>> - >>> # Conservative (slower but more accurate) - >>> config = GOSSConfig(top_rate=0.3, other_rate=0.2) # 44% samples - """ - top_rate: float = 0.2 - other_rate: float = 0.1 - seed: int | None = None - - def __post_init__(self): - if not 0 < self.top_rate < 1: - raise ValueError(f"top_rate must be in (0, 1), got {self.top_rate}") - if not 0 < self.other_rate <= 1: - raise ValueError(f"other_rate must be in (0, 1], got {self.other_rate}") - - @property - def effective_sample_rate(self) -> float: - """Effective fraction of samples used.""" - return self.top_rate + self.other_rate * (1 - self.top_rate) - - -@dataclass -class MiniBatchConfig: - """Configuration for mini-batch training. - - Enables training on datasets larger than memory by processing - samples in chunks and accumulating histograms. - - Args: - batch_size: Number of samples to process at a time. - Larger = faster (better GPU utilization) but more memory. - shuffle: Whether to shuffle samples before each epoch. - seed: Random seed for shuffling. - - Example: - >>> # Process 100k samples at a time - >>> config = MiniBatchConfig(batch_size=100_000) - >>> - >>> # With shuffling for better convergence - >>> config = MiniBatchConfig(batch_size=100_000, shuffle=True, seed=42) - """ - batch_size: int = 100_000 - shuffle: bool = False - seed: int | None = None - - def __post_init__(self): - if self.batch_size <= 0: - raise ValueError(f"batch_size must be positive, got {self.batch_size}") - - -@dataclass -class SamplingResult: - """Result of a sampling operation. - - Attributes: - indices: Indices of selected samples. - weights: Sample weights (for upweighting subsampled samples). - n_selected: Number of samples selected. - n_original: Original number of samples. - """ - indices: NDArray[np.int64] - weights: NDArray[np.float32] - n_selected: int - n_original: int - - @property - def sample_rate(self) -> float: - """Fraction of samples selected.""" - return self.n_selected / self.n_original - - -def goss_sample( - grad: NDArray, - hess: NDArray | None = None, - top_rate: float = 0.2, - other_rate: float = 0.1, - seed: int | None = None, -) -> SamplingResult: - """Gradient-based One-Side Sampling (GOSS). - - GOSS keeps all samples with large gradient magnitudes (important for learning) - and randomly samples from the rest. Small-gradient samples are upweighted - to maintain unbiased gradient estimates. - - This gives ~3x speedup with minimal accuracy loss compared to random subsampling. - - Algorithm: - 1. Sort samples by |gradient| - 2. Keep top `top_rate` samples by gradient magnitude - 3. Randomly sample `other_rate` from the rest - 4. Upweight small-gradient samples by (1 - top_rate) / other_rate - - Args: - grad: Gradient array, shape (n_samples,) or (n_samples, n_params). - For multi-parameter distributions, uses sum of absolute gradients. - hess: Hessian array (unused, for API compatibility). - top_rate: Fraction of high-gradient samples to keep (default 0.2). - other_rate: Fraction of low-gradient samples to sample (default 0.1). - seed: Random seed for reproducibility. - - Returns: - SamplingResult with selected indices and weights. - - Example: - >>> grad = compute_gradients(pred, y) - >>> result = goss_sample(grad, top_rate=0.2, other_rate=0.1) - >>> - >>> # Use selected samples for histogram building - >>> hist = build_histogram(X[result.indices], - ... grad[result.indices] * result.weights, - ... hess[result.indices] * result.weights) - - References: - - LightGBM paper: https://papers.nips.cc/paper/6907-lightgbm - """ - n_samples = len(grad) - - # Handle multi-dimensional gradients (e.g., distributional GBDT) - abs_grad = np.sum(np.abs(grad), axis=1) if grad.ndim > 1 else np.abs(grad) - - # Number of samples to keep from each group - n_top = int(n_samples * top_rate) - n_other = int((n_samples - n_top) * other_rate) - - # Handle edge cases - if n_top == 0: - n_top = 1 - if n_other == 0: - n_other = 1 - - # Find top samples by gradient magnitude - # Using argpartition is O(n) vs O(n log n) for full sort - top_indices_unsorted = np.argpartition(abs_grad, -n_top)[-n_top:] - - # Get indices of "other" samples (not in top) - all_indices = np.arange(n_samples) - top_set = set(top_indices_unsorted) - other_indices = np.array([i for i in all_indices if i not in top_set], dtype=np.int64) - - # Random sample from others - rng = np.random.default_rng(seed) - other_sample_indices = rng.choice(other_indices, size=min(n_other, len(other_indices)), replace=False) - - # Combine indices - selected_indices = np.concatenate([top_indices_unsorted, other_sample_indices]) - - # Compute weights - # - Top samples: weight = 1.0 - # - Other samples: upweight to compensate for subsampling - n_top_actual = len(top_indices_unsorted) - n_other_actual = len(other_sample_indices) - n_selected = n_top_actual + n_other_actual - - weights = np.ones(n_selected, dtype=np.float32) - if n_other_actual > 0 and other_rate < 1.0: - # Upweight factor: (1 - top_rate) / other_rate - # This ensures gradient sum is unbiased - upweight = (1 - top_rate) / other_rate - weights[n_top_actual:] = upweight - - return SamplingResult( - indices=selected_indices, - weights=weights, - n_selected=n_selected, - n_original=n_samples, - ) - - -def random_sample( - n_samples: int, - sample_rate: float = 1.0, - seed: int | None = None, -) -> SamplingResult: - """Random subsampling (baseline). - - Simple random sampling without regard to gradient magnitudes. - All selected samples have equal weight. - - Args: - n_samples: Total number of samples. - sample_rate: Fraction of samples to select (0 < rate <= 1). - seed: Random seed for reproducibility. - - Returns: - SamplingResult with selected indices and unit weights. - - Example: - >>> result = random_sample(n_samples=1_000_000, sample_rate=0.3) - >>> X_subset = X[result.indices] - """ - if sample_rate >= 1.0: - # No sampling needed - return SamplingResult( - indices=np.arange(n_samples, dtype=np.int64), - weights=np.ones(n_samples, dtype=np.float32), - n_selected=n_samples, - n_original=n_samples, - ) - - n_selected = max(1, int(n_samples * sample_rate)) - - rng = np.random.default_rng(seed) - indices = rng.choice(n_samples, size=n_selected, replace=False) - weights = np.ones(n_selected, dtype=np.float32) - - return SamplingResult( - indices=indices, - weights=weights, - n_selected=n_selected, - n_original=n_samples, - ) - - -class MiniBatchIterator: - """Iterator for mini-batch training. - - Yields chunks of sample indices for processing datasets larger than memory. - - Args: - n_samples: Total number of samples. - batch_size: Number of samples per batch. - shuffle: Whether to shuffle indices before iteration. - seed: Random seed for shuffling. - - Example: - >>> iterator = MiniBatchIterator(n_samples=10_000_000, batch_size=100_000) - >>> for batch_indices in iterator: - ... # Load batch data - ... X_batch = load_batch(X_mmap, batch_indices) - ... grad_batch = grad[batch_indices] - ... hess_batch = hess[batch_indices] - ... - ... # Build and accumulate histogram - ... batch_hist = build_histogram(X_batch, grad_batch, hess_batch) - ... total_hist += batch_hist - """ - - def __init__( - self, - n_samples: int, - batch_size: int, - shuffle: bool = False, - seed: int | None = None, - ): - self.n_samples = n_samples - self.batch_size = batch_size - self.shuffle = shuffle - self.seed = seed - - self._indices: NDArray | None = None - self._rng = np.random.default_rng(seed) - - @property - def n_batches(self) -> int: - """Number of batches per epoch.""" - return (self.n_samples + self.batch_size - 1) // self.batch_size - - def __iter__(self): - """Iterate over batch indices.""" - indices = np.arange(self.n_samples, dtype=np.int64) - - if self.shuffle: - self._rng.shuffle(indices) - - self._indices = indices - self._batch_idx = 0 - return self - - def __next__(self) -> NDArray[np.int64]: - """Get next batch of indices.""" - if self._indices is None: - raise StopIteration - - start = self._batch_idx * self.batch_size - if start >= self.n_samples: - self._indices = None - raise StopIteration - - end = min(start + self.batch_size, self.n_samples) - batch_indices = self._indices[start:end] - self._batch_idx += 1 - - return batch_indices - - def __len__(self) -> int: - """Number of batches.""" - return self.n_batches - - -def apply_sampling( - grad: NDArray, - hess: NDArray, - strategy: str | SamplingStrategy = "none", - *, - top_rate: float = 0.2, - other_rate: float = 0.1, - sample_rate: float = 1.0, - seed: int | None = None, -) -> tuple[NDArray, NDArray, NDArray, NDArray]: - """Apply sampling strategy to gradients and hessians. - - Convenience function that applies the specified sampling strategy - and returns the sampled/weighted gradients and hessians. - - Args: - grad: Gradient array, shape (n_samples,). - hess: Hessian array, shape (n_samples,). - strategy: Sampling strategy ("none", "random", "goss"). - top_rate: GOSS top_rate parameter. - other_rate: GOSS other_rate parameter. - sample_rate: Random sampling rate. - seed: Random seed for reproducibility. - - Returns: - Tuple of (indices, weights, sampled_grad, sampled_hess): - - indices: Selected sample indices - - weights: Sample weights - - sampled_grad: Weighted gradients for selected samples - - sampled_hess: Weighted hessians for selected samples - - Example: - >>> indices, weights, grad_s, hess_s = apply_sampling( - ... grad, hess, strategy="goss", top_rate=0.2, other_rate=0.1 - ... ) - >>> # Build histogram with sampled data - >>> hist = build_histogram(X[:, indices], grad_s, hess_s) - """ - strategy = SamplingStrategy(strategy) - - if strategy == SamplingStrategy.NONE: - indices = np.arange(len(grad), dtype=np.int64) - weights = np.ones(len(grad), dtype=np.float32) - return indices, weights, grad, hess - - elif strategy == SamplingStrategy.GOSS: - result = goss_sample(grad, hess, top_rate, other_rate, seed) - - elif strategy == SamplingStrategy.RANDOM: - result = random_sample(len(grad), sample_rate, seed) - - else: - raise ValueError(f"Unknown strategy: {strategy}") - - # Apply sampling and weighting - sampled_grad = grad[result.indices] * result.weights - sampled_hess = hess[result.indices] * result.weights - - return result.indices, result.weights, sampled_grad, sampled_hess - - -# ============================================================================= -# Mini-Batch Histogram Accumulation -# ============================================================================= - -def accumulate_histograms_minibatch( - X_binned, # BinnedArray or memory-mapped - grad: NDArray, - hess: NDArray, - batch_size: int, - n_features: int, - sample_indices: NDArray | None = None, - build_fn: Callable | None = None, -) -> tuple[NDArray, NDArray]: - """Build histograms by accumulating over mini-batches. - - This enables training on datasets larger than GPU/CPU memory by - processing samples in chunks and summing the histograms. - - Args: - X_binned: Binned feature data (can be memory-mapped). - grad: Full gradient array. - hess: Full hessian array. - batch_size: Number of samples per batch. - n_features: Number of features. - sample_indices: Optional indices to use (for node-specific histograms). - build_fn: Function to build histogram for a batch. - Signature: (X_batch, grad_batch, hess_batch) -> (hist_grad, hist_hess) - - Returns: - Accumulated (hist_grad, hist_hess) arrays, shape (n_features, 256). - - Example: - >>> # Training on 100M samples with 8GB GPU - >>> from openboost._core._histogram import build_histogram - >>> - >>> hist_grad, hist_hess = accumulate_histograms_minibatch( - ... X_mmap, grad, hess, - ... batch_size=100_000, - ... n_features=X.shape[1], - ... build_fn=build_histogram, - ... ) - """ - from ._core._histogram import build_histogram as default_build_fn - - if build_fn is None: - build_fn = default_build_fn - - # Initialize accumulated histograms - hist_grad = np.zeros((n_features, 256), dtype=np.float32) - hist_hess = np.zeros((n_features, 256), dtype=np.float32) - - # Determine indices to process - if sample_indices is not None: - indices_to_process = sample_indices - else: - indices_to_process = np.arange(len(grad), dtype=np.int64) - - n_samples = len(indices_to_process) - - # Process in batches - for start in range(0, n_samples, batch_size): - end = min(start + batch_size, n_samples) - batch_indices = indices_to_process[start:end] - - # Extract batch data - # Handle memory-mapped arrays by loading only the batch - if hasattr(X_binned, 'data'): - # BinnedArray - X_batch = X_binned.data[:, batch_indices] - else: - # Raw array (possibly memory-mapped) - X_batch = X_binned[:, batch_indices] - - grad_batch = grad[batch_indices] - hess_batch = hess[batch_indices] - - # Build batch histogram and accumulate - batch_hist_grad, batch_hist_hess = build_fn(X_batch, grad_batch, hess_batch) - - hist_grad += batch_hist_grad - hist_hess += batch_hist_hess - - return hist_grad, hist_hess - - -# ============================================================================= -# Memory-Mapped Array Support -# ============================================================================= - -def create_memmap_binned( - path: str, - X: NDArray, - n_bins: int = 256, -) -> np.memmap: - """Create memory-mapped binned array for large datasets. - - Bins the data and saves to disk as a memory-mapped file, - enabling training on datasets larger than RAM. - - Args: - path: Path to save the memory-mapped file. - X: Input features, shape (n_samples, n_features). - n_bins: Number of bins for quantile binning. - - Returns: - Memory-mapped binned array, shape (n_features, n_samples). - - Example: - >>> # Create once - >>> X_mmap = create_memmap_binned('data.npy', X_train) - >>> - >>> # Load for training (no copy, uses disk) - >>> X_mmap = np.memmap('data.npy', mode='r', dtype=np.uint8, - ... shape=(n_features, n_samples)) - """ - from ._array import array as ob_array - - # Bin the data - binned = ob_array(X, n_bins=n_bins, device='cpu') - - # Get the binned data - data = binned.data.copy_to_host() if hasattr(binned.data, 'copy_to_host') else binned.data - - # Create memory-mapped file - mmap = np.memmap(path, dtype=np.uint8, mode='w+', shape=data.shape) - mmap[:] = data - mmap.flush() - - return mmap - - -def load_memmap_binned( - path: str, - n_features: int, - n_samples: int, -) -> np.memmap: - """Load memory-mapped binned array. - - Args: - path: Path to the memory-mapped file. - n_features: Number of features. - n_samples: Number of samples. - - Returns: - Memory-mapped binned array, shape (n_features, n_samples). - """ - return np.memmap(path, dtype=np.uint8, mode='r', shape=(n_features, n_samples)) - - -__all__ = [ - # Enums and configs - "SamplingStrategy", - "GOSSConfig", - "MiniBatchConfig", - "SamplingResult", - # Sampling functions - "goss_sample", - "random_sample", - "apply_sampling", - # Mini-batch support - "MiniBatchIterator", - "accumulate_histograms_minibatch", - # Memory-mapped support - "create_memmap_binned", - "load_memmap_binned", -] diff --git a/src/openboost/_trainer.py b/src/openboost/_trainer.py deleted file mode 100644 index 6039885..0000000 --- a/src/openboost/_trainer.py +++ /dev/null @@ -1,303 +0,0 @@ -"""Unified multi-channel boosting trainer. - -One loop for every model: bin once, maintain raw scores F (n, K), ask the -objective for per-channel (grad, hess), fit one tree per channel, update F -and every eval set incrementally. -""" - -from __future__ import annotations - -from collections.abc import Callable -from dataclasses import dataclass -from typing import Any - -import numpy as np -from numpy.typing import NDArray - -from ._array import BinnedArray, array -from ._backends import is_cuda -from ._callbacks import ( - Callback, - CallbackManager, - EarlyStopping, - TrainingState, - warn_if_early_stopping_without_eval_set, -) -from ._core._growth import TreeStructure -from ._core._tree import fit_tree, fit_tree_gpu_native -from ._objectives import Objective, RawScores -from ._validation import validate_eval_set, validate_sample_weight - - -@dataclass -class TrainerConfig: - """Tree-building knobs shared by every facade.""" - - n_trees: int = 100 - max_depth: int = 6 - learning_rate: float = 0.1 - min_child_weight: float = 1.0 - reg_lambda: float = 1.0 - reg_alpha: float = 0.0 - subsample: float = 1.0 - colsample_bytree: float = 1.0 - n_bins: int = 254 - - -def _to_host(pred: NDArray) -> NDArray: - if hasattr(pred, "copy_to_host"): - return pred.copy_to_host() - if hasattr(pred, "get"): - return pred.get() - return np.asarray(pred) - - -def _is_device(arr) -> bool: - return hasattr(arr, "__cuda_array_interface__") - - -def _as_host_raw(raw: RawScores) -> RawScores: - return {name: _to_host(score) for name, score in raw.items()} - - -def _gpu_native_eligible(X_binned: BinnedArray, config: TrainerConfig) -> bool: - if not is_cuda(): - return False - if config.reg_alpha != 0.0 or config.colsample_bytree < 1.0 or config.subsample < 1.0: - return False - has_missing = ( - hasattr(X_binned, "has_missing") - and len(X_binned.has_missing) > 0 - and np.any(X_binned.has_missing) - ) - has_categorical = ( - hasattr(X_binned, "is_categorical") - and len(X_binned.is_categorical) > 0 - and np.any(X_binned.is_categorical) - ) - return not has_missing and not has_categorical - - -def _legacy_to_structure(legacy, n_features: int, max_depth: int) -> TreeStructure: - features, thresholds, values, left, right = legacy.to_arrays() - return TreeStructure( - features=features, - thresholds=thresholds, - left_children=left, - right_children=right, - values=values, - n_nodes=len(features), - depth=max_depth, - n_features=n_features, - ) - - -def _empty_raw(n: int, base_scores: dict[str, float]) -> RawScores: - return { - name: np.full(n, score, dtype=np.float32) for name, score in base_scores.items() - } - - -def _bin_features(X, reference: BinnedArray | None, n_bins: int) -> BinnedArray: - if isinstance(X, BinnedArray): - return X - if reference is not None: - return reference.transform(X) - return array(X, n_bins=n_bins) - - -def predict_raw(model: Any, X) -> RawScores: - """Accumulate raw scores from ``model.trees_`` / ``_base_scores``.""" - if not getattr(model, "trees_", None): - raise RuntimeError("Model not fitted. Call fit() first.") - - X_binned = _bin_features(X, getattr(model, "X_binned_", None), model.n_bins) - n = X_binned.n_samples - raw = _empty_raw(n, model._base_scores) - lr = model.learning_rate - for name, trees in model.trees_.items(): - pred = raw[name] - for tree in trees: - pred = pred + lr * _to_host(tree(X_binned)) - raw[name] = pred - return raw - - -def fit_boosting( - model: Any, - objective: Objective, - X, - y: NDArray, - *, - config: TrainerConfig, - sample_weight: NDArray | None = None, - extra: dict[str, Any] | None = None, - callbacks: list[Callback] | None = None, - early_stopping_rounds: int | None = None, - eval_sets: list[dict[str, Any]] | None = None, - eval_fn: Callable[[NDArray, RawScores, dict[str, Any] | None], float] | None = None, - eval_metric_name: str = "loss", -) -> Any: - """Fit ``model`` in place. Returns ``model``. - - ``eval_sets`` entries are dicts with keys ``X``, ``y``, optional ``extra``, - optional ``name`` (defaults to ``eval_0``, ``eval_1``, ...). - """ - y = np.asarray(y).ravel() - n_samples = len(y) - sample_weight = validate_sample_weight(sample_weight, n_samples) - extra = extra or {} - - model.X_binned_ = _bin_features(X, None, config.n_bins) - model.n_features_in_ = model.X_binned_.n_features - model.learning_rate = config.learning_rate - model.n_bins = config.n_bins - - base = objective.init_raw(y, sample_weight, extra) - model._base_scores = dict(base) - model.trees_ = {name: [] for name in objective.channel_names} - - use_gpu = is_cuda() - device_state = ( - use_gpu - and bool(getattr(objective, "device_capable", False)) - and extra.get("log_offset") is None - ) - use_native = _gpu_native_eligible(model.X_binned_, config) - unit_hess = bool(getattr(objective, "unit_hessian", False)) - - if use_gpu: - from numba import cuda - - from ._core._predict import _add_inplace_cuda - - binned_gpu = model.X_binned_.data - if not _is_device(binned_gpu): - binned_gpu = cuda.to_device(binned_gpu) - else: - cuda = None # type: ignore[assignment] - _add_inplace_cuda = None # type: ignore[assignment] - binned_gpu = model.X_binned_.data - - if device_state: - raw = { - name: cuda.to_device(np.full(n_samples, score, dtype=np.float32)) - for name, score in model._base_scores.items() - } - y_step = cuda.to_device(np.ascontiguousarray(y, dtype=np.float32)) - sw_step = ( - cuda.to_device(np.ascontiguousarray(sample_weight, dtype=np.float32)) - if sample_weight is not None - else None - ) - else: - raw = _empty_raw(n_samples, model._base_scores) - y_step = y - sw_step = sample_weight - - cb_list = list(callbacks) if callbacks else [] - if early_stopping_rounds is not None: - cb_list.append(EarlyStopping(patience=early_stopping_rounds, restore_best=True)) - cb_manager = CallbackManager(cb_list) - state = TrainingState(model=model, n_rounds=config.n_trees) - cb_manager.on_train_begin(state) - - prepared_eval: list[tuple[str, BinnedArray, NDArray, dict[str, Any], RawScores]] = [] - if eval_sets: - pairs = [(item["X"], item["y"]) for item in eval_sets] - validate_eval_set(pairs, model.X_binned_.n_features) - for i, item in enumerate(eval_sets): - name = item.get("name", f"eval_{i}") - X_e = _bin_features(item["X"], model.X_binned_, config.n_bins) - y_e = np.asarray(item["y"]).ravel() - extra_e = item.get("extra") or {} - raw_e = _empty_raw(X_e.n_samples, model._base_scores) - prepared_eval.append((name, X_e, y_e, extra_e, raw_e)) - - warn_if_early_stopping_without_eval_set(cb_list, prepared_eval or None) - model.evals_result_ = {name: {eval_metric_name: []} for name, *_ in prepared_eval} - - score_eval = eval_fn or ( - lambda y_e, raw_e, extra_e: objective.loss_value(raw_e, y_e, None, extra_e) - ) - - for round_idx in range(config.n_trees): - if device_state: - grads = objective.step(raw, y_step, sw_step, extra) - else: - grads = objective.step(_as_host_raw(raw), y, sample_weight, extra) - - for name in objective.channel_names: - grad, hess = grads[name] - if use_gpu and not _is_device(grad): - grad = cuda.to_device(np.ascontiguousarray(grad, dtype=np.float32)) - hess = cuda.to_device(np.ascontiguousarray(hess, dtype=np.float32)) - elif not use_gpu: - grad = np.ascontiguousarray(grad, dtype=np.float32) - hess = np.ascontiguousarray(hess, dtype=np.float32) - - if use_native: - pred_buf = raw[name] if device_state else None - legacy = fit_tree_gpu_native( - binned_gpu, - grad, - hess, - max_depth=config.max_depth, - min_child_weight=config.min_child_weight, - reg_lambda=config.reg_lambda, - pred_gpu=pred_buf, - learning_rate=config.learning_rate, - const_hess=1.0 if unit_hess else 0.0, - ) - tree = _legacy_to_structure( - legacy, model.n_features_in_, config.max_depth - ) - if not device_state: - raw[name] = raw[name] + config.learning_rate * _to_host( - tree(model.X_binned_) - ) - else: - tree = fit_tree( - model.X_binned_, - grad, - hess, - max_depth=config.max_depth, - min_child_weight=config.min_child_weight, - reg_lambda=config.reg_lambda, - reg_alpha=config.reg_alpha, - subsample=config.subsample, - colsample_bytree=config.colsample_bytree, - ) - update = tree(model.X_binned_) - if device_state: - if not _is_device(update): - update = cuda.to_device( - np.ascontiguousarray(_to_host(update), dtype=np.float32) - ) - _add_inplace_cuda(raw[name], update, config.learning_rate) - else: - raw[name] = _to_host(raw[name]) + config.learning_rate * _to_host( - update - ) - - model.trees_[name].append(tree) - for _n, X_e, _y_e, _extra_e, raw_e in prepared_eval: - raw_e[name] = raw_e[name] + config.learning_rate * _to_host(tree(X_e)) - - last_metric = None - for name, _X_e, y_e, extra_e, raw_e in prepared_eval: - last_metric = float(score_eval(y_e, raw_e, extra_e)) - model.evals_result_[name][eval_metric_name].append(last_metric) - - state.round_idx = round_idx - if cb_manager.callbacks: - state.train_loss = objective.loss_value( - _as_host_raw(raw), y, sample_weight, extra - ) - if last_metric is not None: - state.val_loss = last_metric - if not cb_manager.on_round_end(state): - break - - cb_manager.on_train_end(state) - return model diff --git a/src/openboost/_utils.py b/src/openboost/_utils.py deleted file mode 100644 index 715954b..0000000 --- a/src/openboost/_utils.py +++ /dev/null @@ -1,1662 +0,0 @@ -"""Utility functions for OpenBoost. - -Phase 20.6: Helper functions for cross-validation, hyperparameter tuning, -and common workflows. - -Phase 22: Evaluation metrics with sample weight support. -Phase 22 Sprint 2: Probabilistic/distributional metrics for uncertainty quantification. - -Example: - >>> import openboost as ob - >>> from openboost.utils import suggest_params, cross_val_predict_proba - >>> - >>> # Get suggested hyperparameters based on dataset - >>> params = suggest_params(X, y, task='regression') - >>> model = ob.OpenBoostRegressor(**params) - >>> - >>> # Out-of-fold predictions for stacking - >>> oof_pred = cross_val_predict_proba(model, X, y, cv=5) - >>> - >>> # Evaluation metrics - >>> from openboost import roc_auc_score, accuracy_score, log_loss_score - >>> auc = roc_auc_score(y_true, y_pred, sample_weight=weights) - >>> - >>> # Probabilistic metrics (Phase 22 Sprint 2) - >>> from openboost import crps_gaussian, brier_score, pinball_loss - >>> crps = crps_gaussian(y_true, mean_pred, std_pred) - >>> brier = brier_score(y_true, y_proba) -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Literal - -import numpy as np - -if TYPE_CHECKING: - from numpy.typing import NDArray - - -# ============================================================================= -# Evaluation Metrics (Phase 22) - Thin wrappers around sklearn -# ============================================================================= - -def _check_sklearn(): - """Check if sklearn is available.""" - try: - import sklearn # noqa: F401 - return True - except ImportError as err: - raise ImportError( - "scikit-learn is required for evaluation metrics. " - "Install with: pip install scikit-learn" - ) from err - - -def roc_auc_score( - y_true: NDArray, - y_score: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute Area Under the ROC Curve (AUC). - - Thin wrapper around sklearn.metrics.roc_auc_score with sample weight support. - - Args: - y_true: True binary labels, shape (n_samples,). Values should be 0 or 1. - y_score: Predicted scores/probabilities, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - AUC score between 0 and 1. Random classifier = 0.5, perfect = 1.0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 0, 1, 1]) - >>> y_score = np.array([0.1, 0.4, 0.35, 0.8]) - >>> ob.roc_auc_score(y_true, y_score) - 0.75 - """ - _check_sklearn() - from sklearn.metrics import roc_auc_score as _sklearn_auc - return float(_sklearn_auc(y_true, y_score, sample_weight=sample_weight)) - - -def accuracy_score( - y_true: NDArray, - y_pred: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute classification accuracy. - - Thin wrapper around sklearn.metrics.accuracy_score with sample weight support. - - Args: - y_true: True labels, shape (n_samples,). - y_pred: Predicted labels, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Accuracy score between 0 and 1. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 1, 1, 0]) - >>> y_pred = np.array([0, 1, 0, 0]) - >>> ob.accuracy_score(y_true, y_pred) - 0.75 - """ - _check_sklearn() - from sklearn.metrics import accuracy_score as _sklearn_acc - return float(_sklearn_acc(y_true, y_pred, sample_weight=sample_weight)) - - -def log_loss_score( - y_true: NDArray, - y_pred: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute log loss (cross-entropy loss) for binary classification. - - Thin wrapper around sklearn.metrics.log_loss with sample weight support. - - Args: - y_true: True binary labels, shape (n_samples,). Values should be 0 or 1. - y_pred: Predicted probabilities for positive class, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Log loss (lower is better). Perfect predictions = 0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 0, 1, 1]) - >>> y_pred = np.array([0.1, 0.2, 0.7, 0.9]) - >>> ob.log_loss_score(y_true, y_pred) - 0.1738... - """ - _check_sklearn() - from sklearn.metrics import log_loss as _sklearn_ll - return float(_sklearn_ll(y_true, y_pred, sample_weight=sample_weight)) - - -def mse_score( - y_true: NDArray, - y_pred: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute Mean Squared Error. - - Thin wrapper around sklearn.metrics.mean_squared_error with sample weight support. - - Args: - y_true: True values, shape (n_samples,). - y_pred: Predicted values, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - MSE (lower is better). Perfect predictions = 0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> y_pred = np.array([1.1, 2.0, 2.8]) - >>> ob.mse_score(y_true, y_pred) - 0.016666... - """ - _check_sklearn() - from sklearn.metrics import mean_squared_error as _sklearn_mse - return float(_sklearn_mse(y_true, y_pred, sample_weight=sample_weight)) - - -def r2_score( - y_true: NDArray, - y_pred: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute R² (coefficient of determination). - - Thin wrapper around sklearn.metrics.r2_score with sample weight support. - - Args: - y_true: True values, shape (n_samples,). - y_pred: Predicted values, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - R² score. Perfect predictions = 1.0, baseline (mean) = 0.0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0, 4.0]) - >>> y_pred = np.array([1.1, 1.9, 3.1, 3.9]) - >>> ob.r2_score(y_true, y_pred) - 0.98 - """ - _check_sklearn() - from sklearn.metrics import r2_score as _sklearn_r2 - return float(_sklearn_r2(y_true, y_pred, sample_weight=sample_weight)) - - -def mae_score( - y_true: NDArray, - y_pred: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute Mean Absolute Error. - - Thin wrapper around sklearn.metrics.mean_absolute_error with sample weight support. - - Args: - y_true: True values, shape (n_samples,). - y_pred: Predicted values, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - MAE (lower is better). Perfect predictions = 0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> y_pred = np.array([1.1, 2.0, 2.8]) - >>> ob.mae_score(y_true, y_pred) - 0.1 - """ - _check_sklearn() - from sklearn.metrics import mean_absolute_error as _sklearn_mae - return float(_sklearn_mae(y_true, y_pred, sample_weight=sample_weight)) - - -def rmse_score( - y_true: NDArray, - y_pred: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute Root Mean Squared Error. - - Thin wrapper around sklearn.metrics.mean_squared_error with squared=False. - - Args: - y_true: True values, shape (n_samples,). - y_pred: Predicted values, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - RMSE (lower is better). Perfect predictions = 0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> y_pred = np.array([1.1, 2.0, 2.8]) - >>> ob.rmse_score(y_true, y_pred) - 0.1291... - """ - _check_sklearn() - from sklearn.metrics import root_mean_squared_error as _sklearn_rmse - return float(_sklearn_rmse(y_true, y_pred, sample_weight=sample_weight)) - - -def f1_score( - y_true: NDArray, - y_pred: NDArray, - *, - average: Literal['binary', 'micro', 'macro', 'weighted'] = 'binary', - sample_weight: NDArray | None = None, -) -> float: - """Compute F1 score (harmonic mean of precision and recall). - - Thin wrapper around sklearn.metrics.f1_score with sample weight support. - - Args: - y_true: True labels, shape (n_samples,). - y_pred: Predicted labels, shape (n_samples,). - average: Averaging method for multi-class: - - 'binary': Only for binary classification. - - 'micro': Global TP, FP, FN counts. - - 'macro': Unweighted mean of per-class F1. - - 'weighted': Weighted mean by support. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - F1 score between 0 and 1. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 1, 1, 0, 1]) - >>> y_pred = np.array([0, 1, 0, 0, 1]) - >>> ob.f1_score(y_true, y_pred) - 0.8 - """ - _check_sklearn() - from sklearn.metrics import f1_score as _sklearn_f1 - return float(_sklearn_f1(y_true, y_pred, average=average, sample_weight=sample_weight)) - - -def precision_score( - y_true: NDArray, - y_pred: NDArray, - *, - average: Literal['binary', 'micro', 'macro', 'weighted'] = 'binary', - sample_weight: NDArray | None = None, -) -> float: - """Compute precision (positive predictive value). - - Thin wrapper around sklearn.metrics.precision_score with sample weight support. - - Args: - y_true: True labels, shape (n_samples,). - y_pred: Predicted labels, shape (n_samples,). - average: Averaging method for multi-class: - - 'binary': Only for binary classification. - - 'micro': Global TP, FP counts. - - 'macro': Unweighted mean of per-class precision. - - 'weighted': Weighted mean by support. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Precision score between 0 and 1. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 1, 1, 0, 1]) - >>> y_pred = np.array([0, 1, 0, 1, 1]) - >>> ob.precision_score(y_true, y_pred) - 0.666... - """ - _check_sklearn() - from sklearn.metrics import precision_score as _sklearn_prec - return float(_sklearn_prec(y_true, y_pred, average=average, sample_weight=sample_weight)) - - -def recall_score( - y_true: NDArray, - y_pred: NDArray, - *, - average: Literal['binary', 'micro', 'macro', 'weighted'] = 'binary', - sample_weight: NDArray | None = None, -) -> float: - """Compute recall (sensitivity, true positive rate). - - Thin wrapper around sklearn.metrics.recall_score with sample weight support. - - Args: - y_true: True labels, shape (n_samples,). - y_pred: Predicted labels, shape (n_samples,). - average: Averaging method for multi-class: - - 'binary': Only for binary classification. - - 'micro': Global TP, FN counts. - - 'macro': Unweighted mean of per-class recall. - - 'weighted': Weighted mean by support. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Recall score between 0 and 1. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 1, 1, 0, 1]) - >>> y_pred = np.array([0, 1, 0, 0, 1]) - >>> ob.recall_score(y_true, y_pred) - 0.666... - """ - _check_sklearn() - from sklearn.metrics import recall_score as _sklearn_rec - return float(_sklearn_rec(y_true, y_pred, average=average, sample_weight=sample_weight)) - - -# ============================================================================= -# Probabilistic/Distributional Metrics (Phase 22 Sprint 2) -# ============================================================================= - -def crps_gaussian( - y_true: NDArray, - mean: NDArray, - std: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute Continuous Ranked Probability Score for Gaussian predictions. - - CRPS is a strictly proper scoring rule that measures the quality of - probabilistic predictions. Lower is better. For Gaussian distributions, - there's a closed-form solution. - - Args: - y_true: True values, shape (n_samples,). - mean: Predicted mean, shape (n_samples,). - std: Predicted standard deviation, shape (n_samples,). Must be > 0. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Mean CRPS (lower is better). Perfect calibration minimizes CRPS. - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> mean = np.array([1.1, 2.0, 2.8]) - >>> std = np.array([0.5, 0.5, 0.5]) - >>> ob.crps_gaussian(y_true, mean, std) - 0.123... - - Notes: - CRPS formula for Gaussian: CRPS(N(μ,σ²), y) = σ * [z*(2*Φ(z) - 1) + 2*φ(z) - 1/√π] - where z = (y - μ) / σ, Φ is CDF, φ is PDF of standard normal. - - For NaturalBoost models, use: - >>> output = model.predict_distribution(X) - >>> mean, std = output.params[:, 0], np.sqrt(output.params[:, 1]) - >>> crps = ob.crps_gaussian(y_true, mean, std) - """ - from scipy import stats - - y_true = np.asarray(y_true, dtype=np.float64) - mean = np.asarray(mean, dtype=np.float64) - std = np.asarray(std, dtype=np.float64) - - if np.any(std <= 0): - raise ValueError("std must be positive") - - # Standardized residual - z = (y_true - mean) / std - - # CRPS for Gaussian: σ * [z * (2*Φ(z) - 1) + 2*φ(z) - 1/√π] - phi = stats.norm.pdf(z) # Standard normal PDF - Phi = stats.norm.cdf(z) # Standard normal CDF - - crps_values = std * (z * (2 * Phi - 1) + 2 * phi - 1 / np.sqrt(np.pi)) - - if sample_weight is not None: - sample_weight = np.asarray(sample_weight) - return float(np.average(crps_values, weights=sample_weight)) - return float(np.mean(crps_values)) - - -def crps_empirical( - y_true: NDArray, - samples: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute CRPS using empirical distribution from Monte Carlo samples. - - For non-Gaussian distributions, CRPS can be estimated from samples. - This is useful for NaturalBoost models with non-Normal distributions. - - Args: - y_true: True values, shape (n_samples,). - samples: Monte Carlo samples, shape (n_samples, n_mc_samples). - Each row contains samples from the predictive distribution. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Mean CRPS estimated from samples (lower is better). - - Example: - >>> model = ob.NaturalBoostGamma(n_trees=100) - >>> model.fit(X_train, y_train) - >>> samples = model.sample(X_test, n_samples=1000) # (n_test, 1000) - >>> crps = ob.crps_empirical(y_test, samples) - - Notes: - Uses the formula: CRPS = E|X - y| - 0.5 * E|X - X'| - where X, X' are independent samples from the predictive distribution. - """ - y_true = np.asarray(y_true) - samples = np.asarray(samples) - - if samples.ndim == 1: - samples = samples.reshape(-1, 1) - - samples.shape[0] - n_mc = samples.shape[1] - - # E|X - y| term - abs_diff_y = np.mean(np.abs(samples - y_true[:, np.newaxis]), axis=1) - - # E|X - X'| term (simplified using sorted samples) - sorted_samples = np.sort(samples, axis=1) - # Use the identity: E|X - X'| = 2 * integral of F(1-F) dx - # Approximated by: 2/m^2 * sum_{i float: - """Compute Brier score for probabilistic binary classification. - - Brier score measures the mean squared error of probability predictions. - It's a strictly proper scoring rule for binary outcomes. - - Args: - y_true: True binary labels, shape (n_samples,). Values should be 0 or 1. - y_prob: Predicted probabilities for positive class, shape (n_samples,). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Brier score (lower is better). Perfect predictions = 0, random = 0.25. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 0, 1, 1]) - >>> y_prob = np.array([0.1, 0.2, 0.8, 0.9]) - >>> ob.brier_score(y_true, y_prob) - 0.025 - - Notes: - Brier score = mean((y_prob - y_true)²) - - Decomposition: Brier = Reliability - Resolution + Uncertainty - - Reliability: calibration error (how well probabilities match frequencies) - - Resolution: how different predictions are from the base rate - - Uncertainty: entropy of the outcome distribution - """ - _check_sklearn() - from sklearn.metrics import brier_score_loss - return float(brier_score_loss(y_true, y_prob, sample_weight=sample_weight)) - - -def pinball_loss( - y_true: NDArray, - y_pred: NDArray, - quantile: float = 0.5, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute pinball loss (quantile loss) for quantile regression. - - Pinball loss is the proper scoring rule for quantile estimation. - At quantile=0.5, it equals MAE. - - Args: - y_true: True values, shape (n_samples,). - y_pred: Predicted quantile values, shape (n_samples,). - quantile: The quantile being predicted, in (0, 1). Default 0.5 (median). - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Pinball loss (lower is better). - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> y_pred_median = np.array([1.1, 2.0, 2.8]) - >>> ob.pinball_loss(y_true, y_pred_median, quantile=0.5) - 0.1 - - >>> # Lower quantile (e.g., 10th percentile) - >>> y_pred_q10 = np.array([0.5, 1.5, 2.0]) - >>> ob.pinball_loss(y_true, y_pred_q10, quantile=0.1) - - Notes: - Pinball loss: L(y, q) = (y - q) * τ if y >= q else (q - y) * (1 - τ) - where τ is the quantile. - - For prediction intervals from NaturalBoost: - >>> lower, upper = model.predict_interval(X, alpha=0.1) # 90% interval - >>> loss_lower = ob.pinball_loss(y, lower, quantile=0.05) - >>> loss_upper = ob.pinball_loss(y, upper, quantile=0.95) - """ - if not 0 < quantile < 1: - raise ValueError(f"quantile must be in (0, 1), got {quantile}") - - y_true = np.asarray(y_true) - y_pred = np.asarray(y_pred) - - residual = y_true - y_pred - loss = np.where(residual >= 0, quantile * residual, (quantile - 1) * residual) - - if sample_weight is not None: - sample_weight = np.asarray(sample_weight) - return float(np.average(loss, weights=sample_weight)) - return float(np.mean(loss)) - - -def interval_score( - y_true: NDArray, - lower: NDArray, - upper: NDArray, - alpha: float = 0.1, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute interval score for prediction intervals. - - Interval score is a strictly proper scoring rule for prediction intervals. - It rewards narrow intervals while penalizing miscoverage. - - Args: - y_true: True values, shape (n_samples,). - lower: Lower bound of prediction interval, shape (n_samples,). - upper: Upper bound of prediction interval, shape (n_samples,). - alpha: Nominal miscoverage rate (0.1 for 90% interval). Default 0.1. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Interval score (lower is better). - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> lower = np.array([0.5, 1.5, 2.5]) - >>> upper = np.array([1.5, 2.5, 3.5]) - >>> ob.interval_score(y_true, lower, upper, alpha=0.1) - 1.0 - - Notes: - Interval Score = (upper - lower) + (2/α) * (lower - y) * I(y < lower) - + (2/α) * (y - upper) * I(y > upper) - - The score combines: - 1. Interval width (prefer narrow intervals) - 2. Penalty for observations below lower bound - 3. Penalty for observations above upper bound - - Use with NaturalBoost: - >>> lower, upper = model.predict_interval(X_test, alpha=0.1) - >>> score = ob.interval_score(y_test, lower, upper, alpha=0.1) - """ - if not 0 < alpha < 1: - raise ValueError(f"alpha must be in (0, 1), got {alpha}") - - y_true = np.asarray(y_true) - lower = np.asarray(lower) - upper = np.asarray(upper) - - # Interval width - width = upper - lower - - # Penalty for y below lower bound - below_lower = np.maximum(0, lower - y_true) - - # Penalty for y above upper bound - above_upper = np.maximum(0, y_true - upper) - - # Interval score - score = width + (2 / alpha) * (below_lower + above_upper) - - if sample_weight is not None: - sample_weight = np.asarray(sample_weight) - return float(np.average(score, weights=sample_weight)) - return float(np.mean(score)) - - -def expected_calibration_error( - y_true: NDArray, - y_prob: NDArray, - n_bins: int = 10, - *, - strategy: Literal['uniform', 'quantile'] = 'uniform', -) -> float: - """Compute Expected Calibration Error (ECE) for probability predictions. - - ECE measures the miscalibration of predicted probabilities. A well-calibrated - model has ECE close to 0. - - Args: - y_true: True binary labels, shape (n_samples,). Values should be 0 or 1. - y_prob: Predicted probabilities for positive class, shape (n_samples,). - n_bins: Number of bins to use. Default 10. - strategy: Binning strategy: - - 'uniform': Bins of equal width in [0, 1]. - - 'quantile': Bins with equal number of samples. - - Returns: - ECE (lower is better). Perfect calibration = 0. - - Example: - >>> import openboost as ob - >>> y_true = np.array([0, 0, 1, 1, 1]) - >>> y_prob = np.array([0.1, 0.3, 0.6, 0.8, 0.9]) - >>> ob.expected_calibration_error(y_true, y_prob) - 0.06 - - Notes: - ECE = Σ (|bin_size| / n) * |accuracy_in_bin - mean_confidence_in_bin| - - For reliability diagrams, use calibration_curve to get bin data. - """ - y_true = np.asarray(y_true) - y_prob = np.asarray(y_prob) - - n_samples = len(y_true) - - if strategy == 'uniform': - bins = np.linspace(0, 1, n_bins + 1) - elif strategy == 'quantile': - quantiles = np.linspace(0, 100, n_bins + 1) - bins = np.percentile(y_prob, quantiles) - bins[0] = 0.0 - bins[-1] = 1.0 - else: - raise ValueError(f"Unknown strategy: {strategy}") - - ece = 0.0 - for i in range(n_bins): - in_bin = (y_prob > bins[i]) & (y_prob <= bins[i + 1]) - if i == 0: - in_bin = (y_prob >= bins[i]) & (y_prob <= bins[i + 1]) - - bin_size = np.sum(in_bin) - if bin_size > 0: - bin_accuracy = np.mean(y_true[in_bin]) - bin_confidence = np.mean(y_prob[in_bin]) - ece += (bin_size / n_samples) * np.abs(bin_accuracy - bin_confidence) - - return float(ece) - - -def calibration_curve( - y_true: NDArray, - y_prob: NDArray, - n_bins: int = 10, - *, - strategy: Literal['uniform', 'quantile'] = 'uniform', -) -> tuple[NDArray, NDArray, NDArray]: - """Compute calibration curve data for reliability diagrams. - - Returns the fraction of positives and mean predicted probability for each bin. - This data can be used to create reliability diagrams. - - Args: - y_true: True binary labels, shape (n_samples,). Values should be 0 or 1. - y_prob: Predicted probabilities for positive class, shape (n_samples,). - n_bins: Number of bins to use. Default 10. - strategy: Binning strategy: - - 'uniform': Bins of equal width in [0, 1]. - - 'quantile': Bins with equal number of samples. - - Returns: - Tuple of (fraction_of_positives, mean_predicted_value, bin_counts): - - fraction_of_positives: Actual fraction of positives in each bin. - - mean_predicted_value: Mean predicted probability in each bin. - - bin_counts: Number of samples in each bin. - - Example: - >>> import openboost as ob - >>> import matplotlib.pyplot as plt - >>> - >>> y_true = np.array([0, 0, 1, 1, 1, 0, 1, 0, 1, 1]) - >>> y_prob = np.array([0.1, 0.2, 0.3, 0.5, 0.6, 0.4, 0.7, 0.3, 0.8, 0.9]) - >>> frac_pos, mean_pred, counts = ob.calibration_curve(y_true, y_prob, n_bins=5) - >>> - >>> # Plot reliability diagram - >>> plt.plot([0, 1], [0, 1], 'k--', label='Perfectly calibrated') - >>> plt.plot(mean_pred, frac_pos, 's-', label='Model') - >>> plt.xlabel('Mean predicted probability') - >>> plt.ylabel('Fraction of positives') - >>> plt.legend() - """ - y_true = np.asarray(y_true) - y_prob = np.asarray(y_prob) - - if strategy == 'uniform': - bins = np.linspace(0, 1, n_bins + 1) - elif strategy == 'quantile': - quantiles = np.linspace(0, 100, n_bins + 1) - bins = np.percentile(y_prob, quantiles) - bins[0] = 0.0 - bins[-1] = 1.0 - else: - raise ValueError(f"Unknown strategy: {strategy}") - - fraction_of_positives = [] - mean_predicted_value = [] - bin_counts = [] - - for i in range(n_bins): - in_bin = (y_prob > bins[i]) & (y_prob <= bins[i + 1]) - if i == 0: - in_bin = (y_prob >= bins[i]) & (y_prob <= bins[i + 1]) - - bin_size = np.sum(in_bin) - if bin_size > 0: - fraction_of_positives.append(np.mean(y_true[in_bin])) - mean_predicted_value.append(np.mean(y_prob[in_bin])) - bin_counts.append(bin_size) - - return ( - np.array(fraction_of_positives), - np.array(mean_predicted_value), - np.array(bin_counts), - ) - - -def negative_log_likelihood( - y_true: NDArray, - mean: NDArray, - std: NDArray, - *, - sample_weight: NDArray | None = None, -) -> float: - """Compute negative log-likelihood for Gaussian predictions. - - NLL is a proper scoring rule for probabilistic predictions. - Lower is better. - - Args: - y_true: True values, shape (n_samples,). - mean: Predicted mean, shape (n_samples,). - std: Predicted standard deviation, shape (n_samples,). Must be > 0. - sample_weight: Sample weights, shape (n_samples,). If None, uniform weights. - - Returns: - Mean negative log-likelihood (lower is better). - - Example: - >>> import openboost as ob - >>> y_true = np.array([1.0, 2.0, 3.0]) - >>> mean = np.array([1.1, 2.0, 2.8]) - >>> std = np.array([0.5, 0.5, 0.5]) - >>> ob.negative_log_likelihood(y_true, mean, std) - 0.92... - - Notes: - NLL = 0.5 * log(2π) + log(σ) + (y - μ)² / (2σ²) - - For NaturalBoost Normal models: - >>> output = model.predict_distribution(X) - >>> mean, var = output.params[:, 0], output.params[:, 1] - >>> nll = ob.negative_log_likelihood(y, mean, np.sqrt(var)) - """ - y_true = np.asarray(y_true, dtype=np.float64) - mean = np.asarray(mean, dtype=np.float64) - std = np.asarray(std, dtype=np.float64) - - if np.any(std <= 0): - raise ValueError("std must be positive") - - # NLL for Gaussian - nll = 0.5 * np.log(2 * np.pi) + np.log(std) + 0.5 * ((y_true - mean) / std) ** 2 - - if sample_weight is not None: - sample_weight = np.asarray(sample_weight) - return float(np.average(nll, weights=sample_weight)) - return float(np.mean(nll)) - - -# ============================================================================= -# PIT Calibration Diagnostics -# ============================================================================= - -def _randomized_pit_discrete(cdf_fn, y: NDArray, seed: int | None) -> NDArray: - """Randomized PIT for integer-valued (count) distributions. - - Draws u_i ~ Uniform(F(y_i - 1), F(y_i)), which is exactly Uniform(0, 1) - under a correctly specified model (Smith 1985; Czado et al. 2009). - """ - rng = np.random.default_rng(seed) - k = np.round(y) - upper = np.asarray(cdf_fn(k), dtype=np.float64) - lower = np.asarray(cdf_fn(k - 1.0), dtype=np.float64) # cdf(-1) == 0 - return rng.uniform(lower, upper) - - -def _tweedie_pit(dist, params: dict, y: NDArray, seed: int | None) -> NDArray: - """PIT for the Tweedie compound Poisson-Gamma distribution. - - Mirrors ``Tweedie.quantile``: point mass P(Y=0) = exp(-λ) plus a - moment-matched Gamma for the positive part. The atom at zero is handled - with the randomized PIT (u ~ Uniform(0, P(Y=0)) when y == 0). - """ - from scipy import stats - - mu = params['mu'] - phi = params['phi'] - power = dist.power - - if not (1 < power < 2): - # Outside the compound Poisson-Gamma range: Normal approximation - # (consistent with Tweedie.quantile) - sigma = np.sqrt(np.asarray(dist.variance(params), dtype=np.float64)) - return stats.norm.cdf(y, loc=mu, scale=sigma) - - lam = np.power(mu, 2 - power) / (phi * (2 - power)) - p0 = np.exp(-lam) - - var = phi * np.power(mu, power) - one_minus_p0 = np.maximum(1.0 - p0, 1e-12) - mean_pos = mu / one_minus_p0 - var_pos = np.maximum((var + mu ** 2) / one_minus_p0 - mean_pos ** 2, 1e-12) - shape = mean_pos ** 2 / var_pos - scale = var_pos / mean_pos - - u = p0 + one_minus_p0 * stats.gamma.cdf(np.maximum(y, 0.0), a=shape, scale=scale) - - at_zero = y <= 0 - if np.any(at_zero): - rng = np.random.default_rng(seed) - u[at_zero] = rng.uniform(0.0, p0[at_zero]) - return u - - -def _numerical_cdf(dist, params: dict, y: NDArray, n_grid: int = 256) -> NDArray: - """CDF via trapezoidal integration of the density exp(-nll). - - Fallback for continuous distributions without a scipy mapping (e.g. - CustomDistribution). Integrates from a far-left quantile (or - mean - 12*std when quantile is unavailable) up to each y_i. - """ - mean = np.asarray(dist.mean(params), dtype=np.float64) - std = np.sqrt(np.asarray(dist.variance(params), dtype=np.float64)) - try: - lo = np.asarray(dist.quantile(params, 1e-9), dtype=np.float64) - except NotImplementedError: - lo = mean - 12.0 * std - lo = np.minimum(lo, y) # degenerate grid -> integral 0 when y_i < lo_i - - n = y.shape[0] - t = np.linspace(0.0, 1.0, n_grid)[None, :] - grid = lo[:, None] + (y - lo)[:, None] * t # (n, n_grid) - - tiled = {k: np.repeat(np.asarray(v, dtype=np.float64), n_grid) - for k, v in params.items()} - density = np.exp(-np.asarray(dist.nll(grid.ravel(), tiled), dtype=np.float64)) - - trapezoid = getattr(np, 'trapezoid', np.trapz) # np.trapz removed in NumPy 2 - return trapezoid(density.reshape(n, n_grid), grid, axis=1) - - -def pit_values( - dist_output: Any, - y: NDArray, - *, - seed: int | None = None, -) -> NDArray: - """Compute Probability Integral Transform (PIT) values u_i = F_i(y_i). - - If the model is well calibrated, evaluating each observation y_i under - its own predictive CDF F_i yields values that are Uniform(0, 1). Deviations - from uniformity diagnose miscalibration: a U-shaped PIT histogram means the - predictive distributions are too narrow (overconfident), a hump-shaped one - means too wide (underconfident), and a sloped one means biased. - - For discrete families (Poisson, NegativeBinomial) the randomized PIT - u_i ~ Uniform(F(y_i - 1), F(y_i)) is used — the standard approach for - count data (Czado, Gneiting & Held 2009). Tweedie's point mass at zero is - also randomized. Pass ``seed`` for reproducibility in these cases. - - Args: - dist_output: Predictive distribution as returned by - ``model.predict_distribution(X)`` — any object with a ``.params`` - dict of per-sample parameter arrays and a ``.distribution`` - instance. If the distribution defines a ``cdf(y, params)`` method - it is used directly; otherwise known families are mapped to - scipy.stats, and unknown continuous families fall back to - numerical integration of ``exp(-nll)``. - y: Observed target values, shape (n_samples,). - seed: Random seed for the randomized PIT (discrete families and the - Tweedie zero mass). Ignored for purely continuous families. - - Returns: - PIT values in [0, 1], shape (n_samples,), dtype float64. - - Example: - >>> import numpy as np - >>> import openboost as ob - >>> rng = np.random.default_rng(0) - >>> mu = rng.normal(size=1000) - >>> y = rng.normal(mu, 1.0) # data truly Normal(mu, 1) - >>> output = ob.DistributionOutput( - ... params={'loc': mu, 'scale': np.ones(1000)}, - ... distribution=ob.Normal(), - ... ) - >>> pit = ob.pit_values(output, y) - >>> _, _, ks, p = ob.pit_histogram(pit) - >>> p > 0.01 # approximately uniform -> calibrated - True - """ - from scipy import stats - - from ._distributions import ( - Gamma, - LogNormal, - NegativeBinomial, - Normal, - Poisson, - StudentT, - Tweedie, - ) - - params = {k: np.asarray(v, dtype=np.float64).ravel() - for k, v in dist_output.params.items()} - dist = dist_output.distribution - y = np.asarray(y, dtype=np.float64).ravel() - - n_samples = next(iter(params.values())).shape[0] - if y.shape[0] != n_samples: - raise ValueError( - f"y has {y.shape[0]} samples but dist_output has {n_samples}" - ) - - if hasattr(dist, 'cdf'): - # Duck-typed escape hatch: distribution provides its own CDF - u = np.asarray(dist.cdf(y, params), dtype=np.float64) - elif isinstance(dist, Normal): - u = stats.norm.cdf(y, loc=params['loc'], scale=params['scale']) - elif isinstance(dist, LogNormal): - u = stats.lognorm.cdf(y, s=params['scale'], scale=np.exp(params['loc'])) - elif isinstance(dist, Gamma): - u = stats.gamma.cdf(y, a=params['concentration'], scale=1.0 / params['rate']) - elif isinstance(dist, StudentT): - u = stats.t.cdf(y, df=params['df'], loc=params['loc'], scale=params['scale']) - elif isinstance(dist, Poisson): - u = _randomized_pit_discrete( - lambda k: stats.poisson.cdf(k, mu=params['rate']), y, seed - ) - elif isinstance(dist, NegativeBinomial): - p = params['r'] / (params['r'] + params['mu']) - u = _randomized_pit_discrete( - lambda k: stats.nbinom.cdf(k, n=params['r'], p=p), y, seed - ) - elif isinstance(dist, Tweedie): - u = _tweedie_pit(dist, params, y, seed) - else: - u = _numerical_cdf(dist, params, y) - - return np.clip(u, 0.0, 1.0) - - -def pit_histogram( - pit: NDArray, - n_bins: int = 20, -) -> tuple[NDArray, NDArray, float, float]: - """Histogram of PIT values plus a Kolmogorov-Smirnov uniformity test. - - A calibrated model produces a flat PIT histogram; the KS test against - Uniform(0, 1) quantifies the deviation. - - Args: - pit: PIT values in [0, 1] from :func:`pit_values`, shape (n_samples,). - n_bins: Number of equal-width histogram bins over [0, 1]. Default 20. - - Returns: - Tuple of (bin_edges, counts, ks_statistic, ks_pvalue): - - bin_edges: Bin edges, shape (n_bins + 1,). - - counts: Observations per bin, shape (n_bins,). - - ks_statistic: KS distance between the empirical PIT CDF and - Uniform(0, 1). 0 = perfectly uniform. - - ks_pvalue: p-value of the KS test. Small values (< 0.01) reject - uniformity, i.e. indicate miscalibration. - - Example: - >>> import numpy as np - >>> import openboost as ob - >>> pit = np.random.default_rng(0).uniform(size=2000) - >>> edges, counts, ks, p = ob.pit_histogram(pit, n_bins=20) - >>> counts.sum() - 2000 - >>> p > 0.01 # uniform sample passes the KS test - True - """ - from scipy import stats - - pit = np.asarray(pit, dtype=np.float64).ravel() - counts, bin_edges = np.histogram(pit, bins=n_bins, range=(0.0, 1.0)) - result = stats.kstest(pit, 'uniform') - return bin_edges, counts, float(result.statistic), float(result.pvalue) - - -def reliability_diagram( - pit: NDArray, - n_bins: int = 10, -) -> tuple[NDArray, NDArray]: - """Coverage-vs-nominal data for a probabilistic reliability diagram. - - For each nominal quantile level q, computes the observed frequency - P(PIT <= q) — the fraction of observations that fell below their - predicted q-th quantile. A calibrated model lies on the diagonal - (observed == nominal). - - Args: - pit: PIT values in [0, 1] from :func:`pit_values`, shape (n_samples,). - n_bins: Number of nominal quantile levels, placed at bin midpoints - (0.5/n_bins, 1.5/n_bins, ..., 1 - 0.5/n_bins). Default 10. - - Returns: - Tuple of (nominal_quantiles, observed_frequencies), each shape - (n_bins,). Plot observed vs nominal against the y=x diagonal. - - Example: - >>> import numpy as np - >>> import openboost as ob - >>> pit = np.random.default_rng(0).uniform(size=5000) - >>> nominal, observed = ob.reliability_diagram(pit, n_bins=10) - >>> bool(np.max(np.abs(observed - nominal)) < 0.05) # near-diagonal - True - >>> # plt.plot(nominal, observed, 's-'); plt.plot([0, 1], [0, 1], 'k--') - """ - pit = np.asarray(pit, dtype=np.float64).ravel() - nominal = (np.arange(1, n_bins + 1) - 0.5) / n_bins - observed = np.mean(pit[None, :] <= nominal[:, None], axis=1) - return nominal, observed - - -class PITRecalibrator: - """Isotonic-regression PIT recalibrator. - - Wraps a fitted ``sklearn.isotonic.IsotonicRegression`` that maps raw PIT - values to calibrated PIT values. Created by :func:`recalibrate_pit`; use - :meth:`transform` to map new raw PIT values (or predictive CDF levels) - into calibrated ones. - """ - - def __init__(self, isotonic: Any): - self._isotonic = isotonic - - def transform(self, u: NDArray) -> NDArray: - """Map raw PIT values to calibrated PIT values. - - Args: - u: Raw PIT values in [0, 1], shape (n_samples,). - - Returns: - Calibrated PIT values in [0, 1], shape (n_samples,), dtype float64. - """ - u = np.asarray(u, dtype=np.float64).ravel() - return np.clip(self._isotonic.predict(u), 0.0, 1.0) - - -def recalibrate_pit( - pit_calibration: NDArray, - *, - out_of_bounds: Literal['clip', 'nan', 'raise'] = 'clip', -) -> PITRecalibrator: - """Fit an isotonic-regression recalibrator on held-out PIT values. - - Learns the monotone map T(u) = empirical CDF of the calibration PIT - values. If the model's raw PIT is not uniform (miscalibrated), applying - T to future PIT values makes them approximately uniform — equivalently, - T recalibrates the model's predictive quantile levels (Kuleshov, - Fenner & Ermon 2018). - - Requires scikit-learn (an optional extra). - - Args: - pit_calibration: Raw PIT values from a held-out calibration set, - shape (n_samples,), as returned by :func:`pit_values`. Must - contain at least 2 values. - out_of_bounds: How the recalibrator handles inputs outside the - calibration range: 'clip' (default), 'nan', or 'raise'. - - Returns: - A fitted :class:`PITRecalibrator` with a ``.transform(u)`` method. - - Raises: - ImportError: If scikit-learn is not installed. - ValueError: If fewer than 2 calibration values are given. - - Example: - >>> import numpy as np - >>> import openboost as ob - >>> rng = np.random.default_rng(0) - >>> # Overconfident model: claims scale 1.0 but data has scale 1.5 - >>> mu = rng.normal(size=4000) - >>> y = rng.normal(mu, 1.5) - >>> output = ob.DistributionOutput( - ... params={'loc': mu, 'scale': np.ones(4000)}, - ... distribution=ob.Normal(), - ... ) - >>> pit = ob.pit_values(output, y) - >>> recal = ob.recalibrate_pit(pit[:2000]) # fit on first half - >>> calibrated = recal.transform(pit[2000:]) # apply to second half - >>> _, _, ks_raw, _ = ob.pit_histogram(pit[2000:]) - >>> _, _, ks_cal, _ = ob.pit_histogram(calibrated) - >>> ks_cal < ks_raw # recalibration improves uniformity - True - """ - try: - from sklearn.isotonic import IsotonicRegression - except ImportError as err: - raise ImportError( - "scikit-learn is required for recalibrate_pit. " - "Install with: uv sync --extra dev (or pip install scikit-learn)" - ) from err - - pit = np.asarray(pit_calibration, dtype=np.float64).ravel() - if pit.size < 2: - raise ValueError( - f"recalibrate_pit needs at least 2 calibration values, got {pit.size}" - ) - - order = np.argsort(pit) - # Hazen plotting positions: empirical CDF evaluated at the sorted PITs - targets = (np.arange(1, pit.size + 1) - 0.5) / pit.size - - iso = IsotonicRegression( - y_min=0.0, y_max=1.0, increasing=True, out_of_bounds=out_of_bounds - ) - iso.fit(pit[order], targets) - return PITRecalibrator(iso) - - -# Suggested parameter grids for hyperparameter tuning -PARAM_GRID_REGRESSION = { - 'n_estimators': [100, 300, 500], - 'max_depth': [4, 6, 8], - 'learning_rate': [0.01, 0.05, 0.1], - 'subsample': [0.7, 0.8, 1.0], - 'colsample_bytree': [0.7, 0.8, 1.0], - 'reg_lambda': [0.1, 1.0, 10.0], -} - -PARAM_GRID_CLASSIFICATION = { - 'n_estimators': [100, 300, 500], - 'max_depth': [3, 5, 7], - 'learning_rate': [0.01, 0.05, 0.1], - 'subsample': [0.7, 0.8, 1.0], - 'colsample_bytree': [0.7, 0.8, 1.0], - 'reg_lambda': [0.1, 1.0, 10.0], -} - -PARAM_GRID_DISTRIBUTIONAL = { - 'n_estimators': [100, 200, 500], - 'max_depth': [3, 4, 5], # Typically shallower for distributional - 'learning_rate': [0.05, 0.1, 0.2], - 'reg_lambda': [0.1, 1.0, 5.0], -} - - -def suggest_params( - X: NDArray, - y: NDArray, - task: Literal['regression', 'classification', 'distributional'] = 'regression', - n_estimators_cap: int = 500, - style: Literal['sklearn', 'core'] = 'sklearn', -) -> dict[str, Any]: - """Suggest hyperparameters based on dataset characteristics. - - This provides reasonable starting points based on heuristics. For best - results, use these as initial values and tune with cross-validation. - - Args: - X: Feature matrix, shape (n_samples, n_features). - y: Target values, shape (n_samples,). - task: Type of task - 'regression', 'classification', or 'distributional'. - n_estimators_cap: Maximum number of estimators to suggest. - style: Parameter naming style. 'sklearn' returns names like - ``n_estimators`` for use with sklearn wrappers. 'core' returns - names like ``n_trees`` for use with GradientBoosting directly. - - Returns: - Dictionary of suggested hyperparameters. - - Example: - >>> # For sklearn wrappers (default) - >>> params = suggest_params(X_train, y_train, task='regression') - >>> model = OpenBoostRegressor(**params) - >>> model.fit(X_train, y_train) - >>> - >>> # For core API - >>> params = suggest_params(X_train, y_train, style='core') - >>> model = GradientBoosting(**params) - >>> model.fit(X_train, y_train) - - Notes: - - For small datasets (< 1000 samples): Fewer trees, more regularization - - For large datasets (> 100k samples): More trees, lower learning rate - - For high-dimensional data: More column sampling, shallower trees - - For noisy data: Consider distributional models for uncertainty - """ - X = np.asarray(X) - y = np.asarray(y) - n_samples, n_features = X.shape - - # Use y to determine task type if task is not explicitly set - # and to inform parameter suggestions - unique_y = np.unique(y) - n_unique = len(unique_y) - - # Detect task type from y if classification - is_multiclass = n_unique > 2 and n_unique <= 50 and task == 'classification' - is_imbalanced = False - if task == 'classification' and n_unique <= 50: - class_counts = np.array([np.sum(y == c) for c in unique_y]) - imbalance_ratio = class_counts.max() / class_counts.min() - is_imbalanced = imbalance_ratio > 5 - - # Base parameters - params: dict[str, Any] = {} - - # Number of trees: scale with data size, but cap - if n_samples < 1000: - params['n_estimators'] = min(100, n_estimators_cap) - elif n_samples < 10000: - params['n_estimators'] = min(200, n_estimators_cap) - elif n_samples < 100000: - params['n_estimators'] = min(300, n_estimators_cap) - else: - params['n_estimators'] = min(500, n_estimators_cap) - - # Learning rate: lower for more trees - if params['n_estimators'] >= 300: - params['learning_rate'] = 0.05 - else: - params['learning_rate'] = 0.1 - - # Tree depth: based on features and task - if task == 'distributional': - # Distributional models work better with shallower trees - params['max_depth'] = min(5, 3 + n_features // 50) - elif n_features > 100: - # High-dimensional: shallower trees, more regularization - params['max_depth'] = min(6, 4 + n_features // 100) - else: - params['max_depth'] = min(8, 4 + n_features // 20) - - # Regularization: more for small datasets - if n_samples < 1000: - params['reg_lambda'] = 10.0 - params['min_child_weight'] = 3.0 - elif n_samples < 10000: - params['reg_lambda'] = 1.0 - params['min_child_weight'] = 1.0 - else: - params['reg_lambda'] = 0.1 - params['min_child_weight'] = 1.0 - - # Sampling: use for larger datasets - if n_samples > 10000: - params['subsample'] = 0.8 - params['colsample_bytree'] = 0.8 - - # High-dimensional: more column sampling - if n_features > 100: - params['colsample_bytree'] = 0.6 - - # Adjust for imbalanced classification - if is_imbalanced: - # More trees and lower learning rate help with imbalanced data - params['n_estimators'] = min(params['n_estimators'] + 100, n_estimators_cap) - params['learning_rate'] = min(params['learning_rate'], 0.05) - - # Multiclass may benefit from shallower trees - if is_multiclass and n_unique > 10: - params['max_depth'] = min(params['max_depth'], 6) - - if style == 'core': - _sklearn_to_core = {'n_estimators': 'n_trees'} - params = {_sklearn_to_core.get(k, k): v for k, v in params.items()} - - return params - - -def cross_val_predict( - model: Any, - X: NDArray, - y: NDArray, - cv: int = 5, - random_state: int | None = 42, -) -> NDArray: - """Generate out-of-fold predictions using cross-validation. - - Each sample gets a prediction from a model that was not trained on it. - Useful for stacking/blending in competitions and for honest evaluation. - - Args: - model: An OpenBoost model instance (will be cloned for each fold). - X: Feature matrix, shape (n_samples, n_features). - y: Target values, shape (n_samples,). - cv: Number of cross-validation folds. - random_state: Random seed for reproducible fold splits. - - Returns: - Out-of-fold predictions, shape (n_samples,) for regression or - shape (n_samples, n_classes) for classification probabilities. - - Example: - >>> from openboost import OpenBoostRegressor - >>> from openboost.utils import cross_val_predict - >>> - >>> model = OpenBoostRegressor(n_estimators=100) - >>> oof_pred = cross_val_predict(model, X, y, cv=5) - >>> - >>> # Use OOF predictions for stacking - >>> from sklearn.linear_model import Ridge - >>> meta_model = Ridge() - >>> meta_model.fit(oof_pred.reshape(-1, 1), y) - """ - try: - from sklearn.base import clone - from sklearn.model_selection import KFold - except ImportError as err: - raise ImportError( - "sklearn is required for cross_val_predict. " - "Install with: pip install scikit-learn" - ) from err - - X = np.asarray(X) - y = np.asarray(y) - n_samples = len(y) - - kf = KFold(n_splits=cv, shuffle=True, random_state=random_state) - - # First fold to determine output shape - first_train, first_val = next(iter(kf.split(X))) - model_clone = clone(model) - model_clone.fit(X[first_train], y[first_train]) - first_pred = model_clone.predict(X[first_val]) - - # Initialize output array - if first_pred.ndim == 1: - oof_pred = np.zeros(n_samples, dtype=np.float32) - else: - oof_pred = np.zeros((n_samples, first_pred.shape[1]), dtype=np.float32) - - oof_pred[first_val] = first_pred - - # Remaining folds - for i, (train_idx, val_idx) in enumerate(kf.split(X)): - if i == 0: - continue # Already done first fold - model_clone = clone(model) - model_clone.fit(X[train_idx], y[train_idx]) - oof_pred[val_idx] = model_clone.predict(X[val_idx]) - - return oof_pred - - -def cross_val_predict_proba( - model: Any, - X: NDArray, - y: NDArray, - cv: int = 5, - random_state: int | None = 42, -) -> NDArray: - """Generate out-of-fold probability predictions using cross-validation. - - Similar to cross_val_predict but returns class probabilities instead - of class labels. Only works with classifiers. - - Args: - model: An OpenBoost classifier instance (must have predict_proba). - X: Feature matrix, shape (n_samples, n_features). - y: Target labels, shape (n_samples,). - cv: Number of cross-validation folds. - random_state: Random seed for reproducible fold splits. - - Returns: - Out-of-fold probability predictions, shape (n_samples, n_classes). - - Example: - >>> from openboost import OpenBoostClassifier - >>> from openboost.utils import cross_val_predict_proba - >>> - >>> model = OpenBoostClassifier(n_estimators=100) - >>> oof_proba = cross_val_predict_proba(model, X, y, cv=5) - >>> - >>> # Use probabilities for stacking - >>> meta_features = oof_proba[:, 1] # P(class=1) - - Raises: - AttributeError: If model doesn't have predict_proba method. - """ - try: - from sklearn.base import clone - from sklearn.model_selection import StratifiedKFold - except ImportError as err: - raise ImportError( - "sklearn is required for cross_val_predict_proba. " - "Install with: pip install scikit-learn" - ) from err - - if not hasattr(model, 'predict_proba'): - raise AttributeError( - f"{type(model).__name__} doesn't have predict_proba method. " - "Use cross_val_predict for regressors." - ) - - X = np.asarray(X) - y = np.asarray(y) - n_samples = len(y) - - kf = StratifiedKFold(n_splits=cv, shuffle=True, random_state=random_state) - - # First fold to determine number of classes - first_train, first_val = next(iter(kf.split(X, y))) - model_clone = clone(model) - model_clone.fit(X[first_train], y[first_train]) - first_proba = model_clone.predict_proba(X[first_val]) - n_classes = first_proba.shape[1] - - oof_proba = np.zeros((n_samples, n_classes), dtype=np.float32) - oof_proba[first_val] = first_proba - - # Remaining folds - for i, (train_idx, val_idx) in enumerate(kf.split(X, y)): - if i == 0: - continue - model_clone = clone(model) - model_clone.fit(X[train_idx], y[train_idx]) - oof_proba[val_idx] = model_clone.predict_proba(X[val_idx]) - - return oof_proba - - -def cross_val_predict_interval( - model: Any, - X: NDArray, - y: NDArray, - alpha: float = 0.1, - cv: int = 5, - random_state: int | None = 42, -) -> tuple[NDArray, NDArray]: - """Generate out-of-fold prediction intervals using cross-validation. - - For distributional models that support uncertainty quantification. - Returns lower and upper bounds of the prediction interval. - - Args: - model: An OpenBoost distributional model (must have predict_interval). - X: Feature matrix, shape (n_samples, n_features). - y: Target values, shape (n_samples,). - alpha: Significance level (0.1 = 90% interval). - cv: Number of cross-validation folds. - random_state: Random seed for reproducible fold splits. - - Returns: - Tuple of (lower_bounds, upper_bounds), each shape (n_samples,). - - Example: - >>> from openboost import OpenBoostDistributionalRegressor - >>> from openboost.utils import cross_val_predict_interval - >>> - >>> model = OpenBoostDistributionalRegressor(distribution='normal') - >>> lower, upper = cross_val_predict_interval(model, X, y, alpha=0.1) - >>> - >>> # Check coverage - >>> coverage = np.mean((y >= lower) & (y <= upper)) - >>> print(f"90% interval coverage: {coverage:.2%}") - - Raises: - AttributeError: If model doesn't have predict_interval method. - """ - try: - from sklearn.base import clone - from sklearn.model_selection import KFold - except ImportError as err: - raise ImportError( - "sklearn is required for cross_val_predict_interval. " - "Install with: pip install scikit-learn" - ) from err - - if not hasattr(model, 'predict_interval'): - raise AttributeError( - f"{type(model).__name__} doesn't have predict_interval method. " - "Use a distributional model like OpenBoostDistributionalRegressor." - ) - - X = np.asarray(X) - y = np.asarray(y) - n_samples = len(y) - - oof_lower = np.zeros(n_samples, dtype=np.float32) - oof_upper = np.zeros(n_samples, dtype=np.float32) - - kf = KFold(n_splits=cv, shuffle=True, random_state=random_state) - - for train_idx, val_idx in kf.split(X): - model_clone = clone(model) - model_clone.fit(X[train_idx], y[train_idx]) - lower, upper = model_clone.predict_interval(X[val_idx], alpha=alpha) - oof_lower[val_idx] = lower - oof_upper[val_idx] = upper - - return oof_lower, oof_upper - - -def evaluate_coverage( - y_true: NDArray, - lower: NDArray, - upper: NDArray, - alpha: float = 0.1, -) -> dict[str, float]: - """Evaluate prediction interval coverage and width. - - Args: - y_true: True target values, shape (n_samples,). - lower: Lower bounds of intervals, shape (n_samples,). - upper: Upper bounds of intervals, shape (n_samples,). - alpha: Expected significance level (for reporting). - - Returns: - Dictionary with: - - coverage: Fraction of true values within intervals - - expected_coverage: Expected coverage (1 - alpha) - - mean_width: Average interval width - - median_width: Median interval width - - Example: - >>> lower, upper = model.predict_interval(X_test, alpha=0.1) - >>> metrics = evaluate_coverage(y_test, lower, upper, alpha=0.1) - >>> print(f"Coverage: {metrics['coverage']:.2%}") - >>> print(f"Mean width: {metrics['mean_width']:.4f}") - """ - y_true = np.asarray(y_true) - lower = np.asarray(lower) - upper = np.asarray(upper) - - in_interval = (y_true >= lower) & (y_true <= upper) - widths = upper - lower - - return { - 'coverage': float(np.mean(in_interval)), - 'expected_coverage': 1.0 - alpha, - 'mean_width': float(np.mean(widths)), - 'median_width': float(np.median(widths)), - } - - -def get_param_grid( - task: Literal['regression', 'classification', 'distributional'] = 'regression', -) -> dict[str, list]: - """Get a suggested parameter grid for hyperparameter tuning. - - Args: - task: Type of task - 'regression', 'classification', or 'distributional'. - - Returns: - Dictionary of parameter names to lists of values, suitable for - sklearn's GridSearchCV or RandomizedSearchCV. - - Example: - >>> from sklearn.model_selection import GridSearchCV - >>> from openboost import OpenBoostRegressor - >>> from openboost.utils import get_param_grid - >>> - >>> param_grid = get_param_grid('regression') - >>> search = GridSearchCV(OpenBoostRegressor(), param_grid, cv=3) - >>> search.fit(X, y) - >>> print(search.best_params_) - """ - if task == 'regression': - return PARAM_GRID_REGRESSION.copy() - elif task == 'classification': - return PARAM_GRID_CLASSIFICATION.copy() - elif task == 'distributional': - return PARAM_GRID_DISTRIBUTIONAL.copy() - else: - raise ValueError( - f"Unknown task: {task}. " - "Use 'regression', 'classification', or 'distributional'." - ) diff --git a/src/openboost/_validation.py b/src/openboost/_validation.py deleted file mode 100644 index e719d99..0000000 --- a/src/openboost/_validation.py +++ /dev/null @@ -1,487 +0,0 @@ -"""Input validation utilities for OpenBoost. - -Phase 20.3: Input Validation & Error Messages - -Provides clear, helpful error messages for common user mistakes. -""" - -from __future__ import annotations - -import warnings -from typing import TYPE_CHECKING, Any - -import numpy as np - -if TYPE_CHECKING: - from numpy.typing import NDArray - - - -class ValidationError(ValueError): - """Custom exception for validation errors with helpful messages.""" - - pass - - -def validate_X( - X: Any, - *, - allow_binned: bool = True, - require_2d: bool = True, - ensure_float32: bool = True, - allow_nan: bool = False, - context: str = "fit", -) -> NDArray: - """Validate feature array X. - - Args: - X: Input feature array - allow_binned: Whether to accept BinnedArray - require_2d: Whether X must be 2D - ensure_float32: Whether to convert to float32 - allow_nan: Whether NaN values are allowed - context: Context for error messages ('fit' or 'predict') - - Returns: - Validated (and possibly converted) array - - Raises: - TypeError: If X has wrong type - ValueError: If X has wrong shape or contains invalid values - """ - from ._array import BinnedArray - - # Check type - if isinstance(X, BinnedArray): - if not allow_binned: - raise TypeError( - f"Expected numpy array for {context}, got BinnedArray. " - f"Pass the original numpy array instead." - ) - return X - - if not isinstance(X, np.ndarray): - # Try to convert - try: - X = np.asarray(X) - except Exception as e: - raise TypeError( - f"X must be a numpy array or array-like, got {type(X).__name__}. " - f"Conversion failed: {e}" - ) from e - - # Check dimensions - if require_2d: - if X.ndim == 1: - raise ValueError( - f"X must be 2D with shape (n_samples, n_features), " - f"got 1D array with shape {X.shape}. " - f"Reshape your data using X.reshape(-1, 1) for single feature." - ) - if X.ndim != 2: - raise ValueError( - f"X must be 2D with shape (n_samples, n_features), " - f"got {X.ndim}D array with shape {X.shape}." - ) - - # Check for empty array - if X.size == 0: - raise ValueError( - f"Cannot {context} on empty array. X has shape {X.shape}." - ) - - # Check dtype and convert - if ensure_float32 and X.dtype != np.float32: - original_dtype = X.dtype - X = X.astype(np.float32) - if original_dtype not in (np.float64, np.float32, np.float16): - warnings.warn( - f"X has dtype {original_dtype}, converting to float32. " - f"For best performance, pass float32 arrays directly.", - UserWarning, - stacklevel=3, - ) - - # Check for NaN - if not allow_nan and np.any(np.isnan(X)): - nan_count = np.sum(np.isnan(X)) - nan_cols = np.where(np.any(np.isnan(X), axis=0))[0] - raise ValueError( - f"X contains {nan_count} NaN values in columns {nan_cols.tolist()}. " - f"Options:\n" - f" 1. Impute missing values before training\n" - f" 2. Use ob.array(X) which handles missing values natively\n" - f" 3. Drop rows with missing values" - ) - - # Check for infinity - if np.any(np.isinf(X)): - inf_count = np.sum(np.isinf(X)) - raise ValueError( - f"X contains {inf_count} infinite values. " - f"Replace with finite values or clip to a reasonable range." - ) - - return X - - -def validate_y( - y: Any, - n_samples: int | None = None, - *, - task: str = "regression", - ensure_float32: bool = True, - context: str = "fit", -) -> NDArray: - """Validate target array y. - - Args: - y: Input target array - n_samples: Expected number of samples (must match X) - task: Task type ('regression', 'binary', 'multiclass') - ensure_float32: Whether to convert to float32 - context: Context for error messages - - Returns: - Validated (and possibly converted) array - - Raises: - TypeError: If y has wrong type - ValueError: If y has wrong shape or values - """ - if not isinstance(y, np.ndarray): - try: - y = np.asarray(y) - except Exception as e: - raise TypeError( - f"y must be a numpy array or array-like, got {type(y).__name__}. " - f"Conversion failed: {e}" - ) from e - - # Flatten if needed - if y.ndim == 2 and y.shape[1] == 1: - y = y.ravel() - elif y.ndim != 1: - raise ValueError( - f"y must be 1D array with shape (n_samples,), " - f"got shape {y.shape}. " - f"For multi-output regression, use sklearn's MultiOutputRegressor wrapper." - ) - - # Check for empty - if y.size == 0: - raise ValueError(f"Cannot {context} with empty target array y.") - - # Check sample count matches - if n_samples is not None and len(y) != n_samples: - raise ValueError( - f"X and y have inconsistent number of samples: " - f"X has {n_samples} samples, y has {len(y)} samples." - ) - - # Check for NaN - if np.any(np.isnan(y)): - nan_count = np.sum(np.isnan(y)) - nan_indices = np.where(np.isnan(y))[0][:5] # Show first 5 - raise ValueError( - f"y contains {nan_count} NaN values at indices {nan_indices.tolist()}{'...' if nan_count > 5 else ''}. " - f"Remove or impute missing target values." - ) - - # Check for infinity - if np.any(np.isinf(y)): - raise ValueError( - "y contains infinite values. Replace with finite values." - ) - - # Task-specific validation - if task == "binary": - unique_values = np.unique(y) - if len(unique_values) > 2: - raise ValueError( - f"Binary classification expects 2 classes, " - f"got {len(unique_values)} unique values: {unique_values[:10].tolist()}{'...' if len(unique_values) > 10 else ''}. " - f"Use MultiClassGradientBoosting for multi-class problems." - ) - if not np.all(np.isin(y, [0, 1])): - warnings.warn( - f"Binary classification expects y in {{0, 1}}, " - f"got values {unique_values.tolist()}. Converting.", - UserWarning, - stacklevel=3, - ) - - elif task == "multiclass": - unique_values = np.unique(y) - if not np.issubdtype(y.dtype, np.integer) and not np.allclose(y, y.astype(int)): - raise ValueError( - "Multi-class classification expects integer class labels, " - "got non-integer values. Convert y to integers." - ) - if np.min(y) != 0: - warnings.warn( - f"Multi-class labels should start from 0, " - f"got min={np.min(y)}. Labels will be remapped.", - UserWarning, - stacklevel=3, - ) - - # Convert dtype - if ensure_float32 and y.dtype != np.float32: - y = y.astype(np.float32) - - return y - - -def validate_sample_weight( - sample_weight: Any | None, - n_samples: int, -) -> NDArray | None: - """Validate sample weights. - - Args: - sample_weight: Sample weights or None - n_samples: Expected number of samples - - Returns: - Validated weights or None - - Raises: - ValueError: If weights have wrong shape or invalid values - """ - if sample_weight is None: - return None - - if not isinstance(sample_weight, np.ndarray): - sample_weight = np.asarray(sample_weight, dtype=np.float32) - - if sample_weight.ndim != 1: - raise ValueError( - f"sample_weight must be 1D array, got shape {sample_weight.shape}." - ) - - if len(sample_weight) != n_samples: - raise ValueError( - f"sample_weight has {len(sample_weight)} elements, " - f"but X has {n_samples} samples." - ) - - if np.any(sample_weight < 0): - raise ValueError( - f"sample_weight cannot contain negative values. " - f"Min value: {np.min(sample_weight)}" - ) - - if np.any(np.isnan(sample_weight)): - raise ValueError("sample_weight contains NaN values.") - - return sample_weight.astype(np.float32) - - -def validate_1d(a: Any, n: int, name: str) -> NDArray: - """Validate a length-``n`` 1D float64 array for an auxiliary input. - - Used for per-sample vectors that must align with ``y`` (e.g. a formula's - ``model_input`` or a survival ``event`` indicator). - """ - arr = np.asarray(a, dtype=np.float64).ravel() - if arr.shape[0] != n: - raise ValueError( - f"{name} has length {arr.shape[0]}, expected {n} (matching y)." - ) - return arr - - -def validate_eval_set( - eval_set: list[tuple] | tuple | None, - n_features: int, - task: str = "regression", -) -> list[tuple[NDArray, NDArray]] | None: - """Validate evaluation set for early stopping. - - Args: - eval_set: List of (X, y) tuples, or a single (X, y) tuple. - Both formats are accepted for convenience: - - eval_set=[(X_val, y_val)] # List format - - eval_set=(X_val, y_val) # Single tuple (auto-wrapped) - n_features: Expected number of features - task: Task type for y validation - - Returns: - Validated eval_set as list of tuples, or None - """ - if eval_set is None: - return None - - # Handle single tuple: eval_set=(X_val, y_val) -> [(X_val, y_val)] - # Detect by checking if it's a tuple of 2 arrays (not a list of tuples) - if isinstance(eval_set, tuple) and len(eval_set) == 2: - first_elem = eval_set[0] - # If first element looks like an array (has shape), it's a single (X, y) tuple - if hasattr(first_elem, 'shape') or hasattr(first_elem, '__len__'): - eval_set = [eval_set] - - if not isinstance(eval_set, list): - raise TypeError( - f"eval_set must be a list of (X, y) tuples or a single (X, y) tuple, " - f"got {type(eval_set).__name__}." - ) - - validated = [] - for i, item in enumerate(eval_set): - if not isinstance(item, tuple) or len(item) != 2: - raise ValueError( - f"eval_set[{i}] must be a tuple of (X, y), got {type(item).__name__}." - ) - - X_val, y_val = item - X_val = validate_X(X_val, allow_nan=True, context="eval") - - n_features_val = X_val.n_features if hasattr(X_val, 'n_features') else X_val.shape[1] - if n_features_val != n_features: - raise ValueError( - f"eval_set[{i}] X has {n_features_val} features, " - f"but training data has {n_features} features." - ) - - y_val = validate_y(y_val, n_samples=X_val.shape[0], task=task, context="eval") - validated.append((X_val, y_val)) - - return validated - - -def validate_hyperparameters( - n_trees: int, - max_depth: int, - learning_rate: float, - min_child_weight: float, - reg_lambda: float, - subsample: float, - n_samples: int | None = None, -) -> None: - """Validate hyperparameters and warn about suspicious values. - - Args: - n_trees: Number of trees - max_depth: Maximum tree depth - learning_rate: Learning rate - min_child_weight: Minimum child weight - reg_lambda: L2 regularization - subsample: Row sampling rate - n_samples: Number of samples (for warnings) - - Raises: - ValueError: For invalid hyperparameters - """ - # Hard errors - if n_trees < 1: - raise ValueError(f"n_trees must be >= 1, got {n_trees}.") - - if max_depth < 1: - raise ValueError(f"max_depth must be >= 1, got {max_depth}.") - - if learning_rate <= 0: - raise ValueError(f"learning_rate must be > 0, got {learning_rate}.") - - if min_child_weight < 0: - raise ValueError(f"min_child_weight must be >= 0, got {min_child_weight}.") - - if reg_lambda < 0: - raise ValueError(f"reg_lambda must be >= 0, got {reg_lambda}.") - - if not 0 < subsample <= 1: - raise ValueError(f"subsample must be in (0, 1], got {subsample}.") - - # Warnings for suspicious values - if learning_rate > 1: - warnings.warn( - f"learning_rate={learning_rate} is very high. " - f"Typical values are 0.01-0.3. High values may cause instability.", - UserWarning, - stacklevel=3, - ) - - if max_depth > 15: - warnings.warn( - f"max_depth={max_depth} is very deep. " - f"Deep trees may overfit. Consider max_depth=6-10.", - UserWarning, - stacklevel=3, - ) - - if n_samples is not None: - if n_samples < 100 and n_trees > 50: - warnings.warn( - f"Only {n_samples} samples but {n_trees} trees. " - f"High risk of overfitting. Consider reducing n_trees.", - UserWarning, - stacklevel=3, - ) - - if n_samples < min_child_weight * 10: - warnings.warn( - f"min_child_weight={min_child_weight} is large relative to " - f"{n_samples} samples. Trees may be very shallow.", - UserWarning, - stacklevel=3, - ) - - -def check_is_fitted(model: Any, attributes: list[str]) -> None: - """Check if model is fitted by checking for required attributes. - - Args: - model: Model instance - attributes: List of attribute names that should exist after fitting - - Raises: - ValueError: If model is not fitted - """ - missing = [attr for attr in attributes if not hasattr(model, attr) or getattr(model, attr) is None] - - if missing: - raise ValueError( - f"This {type(model).__name__} instance is not fitted yet. " - f"Call 'fit' with appropriate arguments before using this method." - ) - - -def validate_predict_input( - model: Any, - X: Any, - n_features_expected: int, -) -> NDArray: - """Validate input for predict methods. - - Args: - model: Fitted model - X: Input features - n_features_expected: Expected number of features from training - - Returns: - Validated X array - """ - from ._array import BinnedArray - - # Check if fitted - check_is_fitted(model, ['trees_']) - - # Handle BinnedArray - if isinstance(X, BinnedArray): - if X.n_features != n_features_expected: - raise ValueError( - f"X has {X.n_features} features, but model was trained " - f"with {n_features_expected} features." - ) - return X - - # Validate regular array - X = validate_X(X, allow_nan=True, context="predict") - - if X.shape[1] != n_features_expected: - raise ValueError( - f"X has {X.shape[1]} features, but model was trained " - f"with {n_features_expected} features." - ) - - return X diff --git a/src/openboost/artifacts.py b/src/openboost/artifacts.py new file mode 100644 index 0000000..ef0ae92 --- /dev/null +++ b/src/openboost/artifacts.py @@ -0,0 +1,215 @@ +"""Immutable numeric ensembles with explicit term coefficients and output maps.""" + +import json +from dataclasses import dataclass +from pathlib import Path + +import numpy as np + +from .data import ClassSchema, MixedData, NumericData, _identity, _owned +from .tree import Tree + + +@dataclass(frozen=True, eq=False) +class ConstantTerm: + value: np.ndarray + coefficient: float = 1.0 + + def __post_init__(self): + object.__setattr__(self, "value", _owned(self.value, ndim=1)) + if ( + isinstance(self.coefficient, bool) + or not np.isscalar(self.coefficient) + or not np.isfinite(self.coefficient) + ): + raise ValueError("finite scalar coefficient required") + object.__setattr__(self, "coefficient", float(self.coefficient)) + + +@dataclass(frozen=True, eq=False) +class TreeTerm: + learner: Tree + mapping: np.ndarray + coefficient: float = 1.0 + + def __post_init__(self): + if not isinstance(self.learner, Tree): + raise ValueError("tree learner required") + mapping = _owned(self.mapping, ndim=2) + if mapping.shape[0] != self.learner.output_width: + raise ValueError("learner requires an [L, K] output mapping") + coefficient = ConstantTerm([0], self.coefficient).coefficient + object.__setattr__(self, "mapping", mapping) + object.__setattr__(self, "coefficient", coefficient) + + +@dataclass(frozen=True, eq=False) +class Model: + """Numeric raw ensemble; observation offsets are supplied at inference time. + + This replaces the B03 constant-only format. Explicit [L, K] maps let vector + learners update multiple raw columns; constants update the entire base width. + """ + + feature_names: tuple[str, ...] + base: np.ndarray + terms: tuple[ConstantTerm | TreeTerm, ...] = () + classes: ClassSchema | None = None + + def __post_init__(self): + names = tuple(self.feature_names) + if ( + not names + or any(not isinstance(n, str) or not n for n in names) + or len(set(names)) != len(names) + ): + raise ValueError("unique nonempty feature names required") + base = _owned(self.base, ndim=1) + if self.classes is not None and ( + not isinstance(self.classes, ClassSchema) + or not ( + (len(self.classes.values) == 2 and len(base) == 1) + or len(base) == len(self.classes.values) + ) + ): + raise ValueError("classifier raw width must be one binary logit or one logit per class") + terms = tuple(self.terms) + bound = np.abs(base).copy() + with np.errstate(over="raise", invalid="raise"): + for term in terms: + if isinstance(term, ConstantTerm): + if term.value.shape != base.shape: + raise ValueError("constant term output width differs from base") + bound += abs(term.coefficient) * np.abs(term.value) + elif isinstance(term, TreeTerm): + if term.learner.binning.feature_names != names or term.mapping.shape != ( + term.learner.output_width, + len(base), + ): + raise ValueError("tree schema or output mapping differs from model") + # Conservative finite envelope over every leaf and every input. + magnitude = np.max( + np.abs(term.learner.value[term.learner.feature == -1]), axis=0 + ) + bound += abs(term.coefficient) * (magnitude @ np.abs(term.mapping)) + else: + raise ValueError("unsupported model term") + object.__setattr__(self, "feature_names", names) + object.__setattr__(self, "base", base) + object.__setattr__(self, "terms", terms) + + def predict(self, data, *, offset=None): + if ( + not isinstance(data, (NumericData, MixedData)) + or data.feature_names != self.feature_names + ): + raise ValueError("inference feature schema differs from model") + raw = np.broadcast_to(self.base, (len(data.values), len(self.base))).copy() + with np.errstate(over="raise", invalid="raise"): + for term in self.terms: + delta = ( + term.value + if isinstance(term, ConstantTerm) + else term.learner.predict(data) @ term.mapping + ) + raw += term.coefficient * delta + if offset is not None: + a = np.asarray(offset, dtype=float) + if a.shape != raw.shape or not np.isfinite(a).all(): + raise ValueError("finite aligned inference offset required") + raw += a + return raw + + def predict_proba(self, data, *, offset=None): + if self.classes is None: + raise ValueError("probabilities require a classification schema") + from .outputs import binary_probabilities, softmax_probabilities + + raw = self.predict(data, offset=offset) + return binary_probabilities(raw) if len(self.base) == 1 else softmax_probabilities(raw) + + def predict_label(self, data, *, offset=None): + probabilities = self.predict_proba(data, offset=offset) + return self.classes.decode(np.argmax(probabilities, axis=1)) + + def record(self): + terms = [] + for term in self.terms: + if isinstance(term, ConstantTerm): + terms.append( + dict(kind="constant", value=term.value.tolist(), coefficient=term.coefficient) + ) + else: + terms.append( + dict( + kind="tree", + learner=term.learner.record(), + mapping=term.mapping.tolist(), + coefficient=term.coefficient, + ) + ) + return dict( + format="openboost-ensemble-v2", + feature_names=list(self.feature_names), + base=self.base.tolist(), + terms=terms, + classes=None if self.classes is None else list(self.classes.values), + ) + + @property + def identity(self): + return _identity(self.record()) + + def save(self, path): + Path(path).write_text(json.dumps(self.record(), allow_nan=False) + "\n") + + @classmethod + def load(cls, path): + def pairs(items): + result = {} + for key, value in items: + if key in result: + raise ValueError("duplicate artifact field") + result[key] = value + return result + + record = json.loads(Path(path).read_text(), object_pairs_hook=pairs) + return cls.from_record(record) + + @classmethod + def from_record(cls, record): + """Validate a nested raw model without temporary files.""" + if ( + not isinstance(record, dict) + or set(record) != {"format", "feature_names", "base", "terms", "classes"} + or record["format"] != "openboost-ensemble-v2" + or any( + not isinstance(record[name], list) for name in ("feature_names", "base", "terms") + ) + ): + raise ValueError("unsupported or corrupt artifact schema") + if record["classes"] is not None and not isinstance(record["classes"], list): + raise ValueError("invalid class schema record") + classes = None if record["classes"] is None else ClassSchema(record["classes"]) + terms = [] + for term in record["terms"]: + if not isinstance(term, dict): + raise ValueError("invalid artifact term") + if term.get("kind") == "constant" and set(term) == {"kind", "value", "coefficient"}: + terms.append(ConstantTerm(term["value"], term["coefficient"])) + elif term.get("kind") == "tree" and set(term) == { + "kind", + "learner", + "mapping", + "coefficient", + }: + terms.append( + TreeTerm( + Tree.from_record(term["learner"]), + term["mapping"], + term["coefficient"], + ) + ) + else: + raise ValueError("invalid artifact term schema") + return cls(record["feature_names"], record["base"], tuple(terms), classes) diff --git a/src/openboost/binning.py b/src/openboost/binning.py new file mode 100644 index 0000000..6a487c1 --- /dev/null +++ b/src/openboost/binning.py @@ -0,0 +1,170 @@ +"""Train-only numeric quantiles/category dictionaries and feature-major CPU codes.""" + +from dataclasses import dataclass, field + +import numpy as np + +from .data import MixedData, NumericData, _identity, category_token + + +def _array(value, dtype): + a = np.asarray(value, dtype=dtype, order="C") + return np.frombuffer(a.tobytes(), dtype=a.dtype).reshape(a.shape) + + +@dataclass(frozen=True, eq=False) +class Binning: + feature_names: tuple[str, ...] + cuts: tuple[np.ndarray, ...] + categories: tuple[tuple | None, ...] | None = None + identity: str = field(init=False) + + def __post_init__(self): + names = tuple(self.feature_names) + cuts = tuple(_array(c, " 1 + or len(set(tokens)) != len(tokens) + or tokens != tuple(sorted(tokens)) + or len(cuts[f]) + or len(tokens) > np.iinfo(np.int32).max + ): + raise ValueError("sorted unique typed categories and no numeric cuts required") + normalized.append(tokens) + categories = tuple(normalized) + object.__setattr__(self, "categories", categories) + object.__setattr__(self, "feature_names", names) + object.__setattr__(self, "cuts", cuts) + object.__setattr__( + self, "identity", _identity("mixed-binning-v1", names, categories, *cuts) + ) + + @classmethod + def fit(cls, data, *, bins=254): + if not isinstance(data, (NumericData, MixedData)): + raise ValueError("numeric or mixed CPU data required") + if type(bins) is not int or not 1 <= bins <= np.iinfo(np.int32).max: + raise ValueError("positive int32 bin capacity required") + cuts, categories = [], [] + for kind, column in zip(data.feature_kinds, data.values.T, strict=True): + if kind == "categorical": + present = [category_token(v) for v in column if category_token(v) is not None] + categories.append(tuple(sorted(set(present)))) + cuts.append(np.array([])) + continue + categories.append(None) + column = np.asarray(column, dtype=float) + observed = column[~np.isnan(column)] + q = ( + np.quantile(observed, np.arange(1, bins) / bins, method="linear") + if len(observed) + else np.array([]) + ) + if not np.isfinite(q).all(): + raise ValueError("nonfinite interpolated cuts; rescale numeric features") + cuts.append( + np.unique(q[(q >= observed.min()) & (q < observed.max())]) if len(observed) else q + ) + return cls(data.feature_names, tuple(cuts), tuple(categories)) + + @property + def feature_kinds(self): + return tuple("numeric" if c is None else "categorical" for c in self.categories) + + @property + def bin_counts(self): + return tuple( + len(cut) + 1 if cat is None else max(1, len(cat)) + for cut, cat in zip(self.cuts, self.categories, strict=True) + ) + + def transform(self, data): + return BinnedData(data, self) + + +@dataclass(frozen=True, eq=False) +class BinnedData: + data: NumericData | MixedData + binning: Binning + codes: np.ndarray = field(init=False) + missing: np.ndarray = field(init=False) + identity: str = field(init=False) + + def __post_init__(self): + if ( + not isinstance(self.data, (NumericData, MixedData)) + or not isinstance(self.binning, Binning) + or self.data.feature_names != self.binning.feature_names + or self.data.feature_kinds != self.binning.feature_kinds + ): + raise ValueError("data and binning schema differ") + codes, missing = [], [] + for f, column in enumerate(self.data.values.T): + categories = self.binning.categories[f] + if categories is None: + column = np.asarray(column, dtype=float) + mask = np.isnan(column) + code = np.where(mask, 0, np.searchsorted(self.binning.cuts[f], column, side="left")) + else: + tokens = tuple(category_token(v) for v in column) + mapping = {token: i for i, token in enumerate(categories)} + mask = np.array([token not in mapping for token in tokens]) + code = np.array([mapping.get(token, 0) for token in tokens]) + codes.append(code) + missing.append(mask) + object.__setattr__(self, "codes", _array(codes, " 0 + if not np.any(keep & (frequency.weight > 0)): + raise ValueError("positive-weight paid policies required for severity") + subset = ( + MixedData(data.values[keep], data.row_ids[keep], data.feature_names, data.feature_kinds) + if isinstance(data, MixedData) + else NumericData(data.values[keep], data.row_ids[keep], data.feature_names) + ) + with np.errstate(over="raise", invalid="raise", divide="raise"): + severity = Problem( + subset, + (amount[keep] / count[keep])[:, None], + subset.row_ids, + weight=frequency.weight[keep] * count[keep], + ) + if np.any(severity.target <= 0): + raise ValueError("positive paid severity underflows float64") + return frequency, severity + + +@dataclass(frozen=True, eq=False) +class FrequencySeverity: + """Declared positive-payment log-rate and log-severity raw models. + + This declaration cannot prove the models' training targets; use matched + paid-loss problems. No output is a full aggregate distribution. + """ + + frequency: Model + severity: Model + + def __post_init__(self): + if any( + not isinstance(m, Model) or len(m.base) != 1 or m.classes is not None + for m in (self.frequency, self.severity) + ): + raise ValueError("two scalar regression models required") + + def predict( + self, + frequency_data, + severity_data, + exposure, + *, + frequency_offset=None, + severity_offset=None, + ): + if not np.array_equal(frequency_data.row_ids, severity_data.row_ids): + raise ValueError("frequency/severity policy row IDs must align in order") + frequency = poisson_mean( + self.frequency.predict(frequency_data, offset=frequency_offset), exposure + ) + severity = positive_mean(self.severity.predict(severity_data, offset=severity_offset)) + with np.errstate(over="raise", invalid="raise"): + annualized = frequency["rate"] * severity + period = frequency["count_mean"] * severity + if np.any(annualized <= 0) or np.any(period <= 0): + raise ValueError("composed positive means underflow float64") + return dict( + paid_count_rate=frequency["rate"], + paid_count_mean=frequency["count_mean"], + severity_mean=severity, + annualized_mean=_owned(annualized, ndim=1), + period_mean=_owned(period, ndim=1), + ) + + def record(self): + return dict( + format="openboost-frequency-severity-v1", + frequency=self.frequency.record(), + severity=self.severity.record(), + ) + + @property + def identity(self): + return _identity(self.record()) + + def save(self, path): + Path(path).write_text(json.dumps(self.record(), allow_nan=False) + "\n") + + @classmethod + def load(cls, path): + def pairs(items): + result = {} + for key, value in items: + if key in result: + raise ValueError("duplicate composition artifact field") + result[key] = value + return result + + record = json.loads(Path(path).read_text(), object_pairs_hook=pairs) + if ( + not isinstance(record, dict) + or set(record) != {"format", "frequency", "severity"} + or record["format"] != "openboost-frequency-severity-v1" + ): + raise ValueError("unsupported composition artifact") + return cls(Model.from_record(record["frequency"]), Model.from_record(record["severity"])) diff --git a/src/openboost/data.py b/src/openboost/data.py new file mode 100644 index 0000000..c24089e --- /dev/null +++ b/src/openboost/data.py @@ -0,0 +1,272 @@ +"""Owned numeric CPU inputs and explicit problem roles (initial B03 contract).""" + +import hashlib +import json +from collections.abc import Mapping +from dataclasses import dataclass, field +from types import MappingProxyType + +import numpy as np + + +def _owned(value, *, ndim, finite=True): + a = np.array(value, dtype=np.float64, copy=True, order="C") + if a.ndim != ndim or not a.size or (finite and not np.isfinite(a).all()): + raise ValueError("nonempty finite array with declared dimensions required") + # A bytes owner prevents callers from re-enabling writeability on the array. + return np.frombuffer(a.tobytes(), dtype=a.dtype).reshape(a.shape) + + +def _identity(*parts): + h = hashlib.sha256() + for part in parts: + if isinstance(part, np.ndarray): + payload = json.dumps([part.dtype.str, part.shape]).encode() + part.tobytes() + else: + payload = json.dumps(part, sort_keys=True, allow_nan=False).encode() + h.update(len(payload).to_bytes(8, "big")) + h.update(payload) + return h.hexdigest() + + +@dataclass(frozen=True, eq=False) +class NumericData: + """Unbinned numeric features; missing values allowed, infinity rejected. + + Owns immutable storage. Row IDs are unique integers; feature names define order. + This does not yet fit a transformer or provide categorical/CUDA data. + """ + + values: np.ndarray + row_ids: np.ndarray + feature_names: tuple[str, ...] + device: str = "cpu" + identity: str = field(init=False) + + def __post_init__(self): + if self.device != "cpu": + raise ValueError("NumericData currently supports CPU only") + x = np.array(self.values, dtype=np.float64, copy=True) + if x.ndim != 2 or not x.size or np.isinf(x).any(): + raise ValueError("nonempty numeric matrix without infinity required") + x[np.isnan(x)] = np.nan + x = _owned(x, ndim=2, finite=False) + ids = np.asarray(self.row_ids) + if ids.shape != (len(x),) or ids.dtype.kind not in "iu" or len(np.unique(ids)) != len(ids): + raise ValueError("aligned unique integer row IDs required") + if np.any(ids > np.iinfo(np.int64).max): + raise ValueError("row ID exceeds int64") + ids = np.frombuffer(ids.astype(" 1: + raise ValueError("mixed category token types are unsupported") + else: + numeric = np.asarray(raw[:, f], dtype=float) + if np.isinf(numeric).any(): + raise ValueError("numeric infinity is unsupported") + column = tuple(None if np.isnan(v) else float(v) for v in numeric) + columns.append(column) + owned = tuple(zip(*columns, strict=True)) + for name, value in ( + ("_values", owned), + ("row_ids", schema.row_ids), + ("feature_names", schema.feature_names), + ("feature_kinds", kinds), + ("device", device), + ( + "identity", + _identity("mixed-cpu-v1", owned, schema.row_ids, schema.feature_names, kinds), + ), + ): + object.__setattr__(self, name, value) + + @property + def values(self): + """Detached object-array export; mutating it cannot change owned tuple state.""" + return np.array(self._values, dtype=object) + + +@dataclass(frozen=True) +class ClassSchema: + values: tuple[str | int, ...] + + def __post_init__(self): + values = tuple(category_token(v) for v in self.values) + if ( + len(values) < 2 + or any(v is None for v in values) + or len({type(v) for v in values}) != 1 + or len(set(values)) != len(values) + or values != tuple(sorted(values)) + ): + raise ValueError("sorted unique homogeneous class labels required") + object.__setattr__(self, "values", values) + + @classmethod + def fit(cls, labels): + labels = tuple(category_token(v) for v in labels) + if not labels or any(v is None for v in labels) or len({type(v) for v in labels}) != 1: + raise ValueError("homogeneous nonmissing training labels required") + return cls(tuple(sorted(set(labels)))) + + def encode(self, labels): + mapping = {v: i for i, v in enumerate(self.values)} + tokens = tuple(category_token(v) for v in labels) + if any(v not in mapping for v in tokens): + raise ValueError("unknown or missing class label") + return _owned([[mapping[v]] for v in tokens], ndim=2) + + def decode(self, codes): + a = np.asarray(codes) + if ( + a.ndim != 1 + or a.dtype.kind not in "iuf" + or not np.isfinite(a).all() + or np.any(a != np.floor(a)) + or np.any(a < 0) + or np.any(a >= len(self.values)) + ): + raise ValueError("in-range integer class codes required") + return tuple(self.values[int(i)] for i in a) + + +@dataclass(frozen=True, eq=False) +class Problem: + """Numeric targets, original row weights and raw offsets in the given row order. + + Targets are [N, T]; offsets are [N, raw_width]. Weight remains [N] and is not applied + here. The caller supplies one explicit row order for all aligned role arrays. + The explicit event_right target kind validates censoring bounds, including upper +infinity. + """ + + data: NumericData | MixedData + target: np.ndarray + row_ids: np.ndarray + weight: np.ndarray | None = None + offset: np.ndarray | None = None + raw_width: int | None = None + structure: Mapping | None = None + classes: ClassSchema | None = None + target_kind: str = "numeric" + identity: str = field(init=False) + + def __post_init__(self): + ids = np.asarray(self.row_ids) + if ids.dtype.kind not in "iu": + raise ValueError("integer role row IDs required") + if not isinstance(self.data, (NumericData, MixedData)) or not np.array_equal( + self.row_ids, self.data.row_ids + ): + raise ValueError("problem roles must match prepared row order") + if self.target_kind not in ("numeric", "event_right"): + raise ValueError("unsupported target kind") + y = _owned(self.target, ndim=2, finite=self.target_kind == "numeric") + if self.target_kind == "event_right" and ( + y.shape[1] != 2 + or not np.isfinite(y[:, 0]).all() + or np.any(y[:, 0] <= 0) + or np.any(~((y[:, 1] == y[:, 0]) | np.isposinf(y[:, 1]))) + or self.classes is not None + ): + raise ValueError("positive event/right-censored lower/upper bounds required") + if len(y) != len(self.data.values): + raise ValueError("target rows differ from data") + if self.classes is not None: + if not isinstance(self.classes, ClassSchema) or y.shape[1] != 1: + raise ValueError("class schema requires encoded scalar labels") + self.classes.decode(y[:, 0]) + w = _owned(np.ones(len(y)) if self.weight is None else self.weight, ndim=1) + if w.shape != (len(y),) or np.any(w < 0) or not np.isfinite(w.sum()) or w.sum() <= 0: + raise ValueError("nonnegative aligned weights with positive finite mass required") + default_width = 1 if self.target_kind == "event_right" else y.shape[1] + width = default_width if self.raw_width is None else self.raw_width + if type(width) is not int or width < 1: + raise ValueError("positive integer raw_width required") + offset = _owned(np.zeros((len(y), width)) if self.offset is None else self.offset, ndim=2) + if offset.shape != (len(y), width): + raise ValueError("offset must match raw parameter shape") + object.__setattr__(self, "raw_width", width) + roles = {} if self.structure is None else self.structure + if not isinstance(roles, Mapping) or any(not isinstance(k, str) or not k for k in roles): + raise ValueError("named structural roles required") + roles = {k: _owned(v, ndim=2) for k, v in roles.items()} + if any(len(v) != len(y) for v in roles.values()): + raise ValueError("structure must match problem row order") + object.__setattr__(self, "structure", MappingProxyType(roles)) + object.__setattr__(self, "target", y) + object.__setattr__(self, "weight", w) + object.__setattr__(self, "offset", offset) + object.__setattr__(self, "row_ids", self.data.row_ids) + object.__setattr__( + self, + "identity", + _identity( + "problem-cpu-v1", + self.target_kind, + self.data.identity, + y, + w, + offset, + tuple((k, _identity(roles[k])) for k in sorted(roles)), + None if self.classes is None else self.classes.values, + ), + ) + + def with_offset(self, raw): + """Return prediction-space raw values; never mutate the cached raw input.""" + raw = np.asarray(raw, dtype=float) + if raw.shape != self.offset.shape or not np.isfinite(raw).all(): + raise ValueError("raw values must match the problem output shape") + with np.errstate(over="raise", invalid="raise"): + return raw + self.offset diff --git a/src/openboost/leaves.py b/src/openboost/leaves.py new file mode 100644 index 0000000..312da36 --- /dev/null +++ b/src/openboost/leaves.py @@ -0,0 +1,105 @@ +"""Owned routed residuals and scalar pinball leaf solvers.""" + +from dataclasses import dataclass + +import numpy as np + +from .binning import _array +from .data import Problem, _owned + + +@dataclass(frozen=True, eq=False) +class ResidualView: + row_ids: np.ndarray + residual: np.ndarray + weight: np.ndarray + + def __post_init__(self): + residual, weight = _owned(self.residual, ndim=1), _owned(self.weight, ndim=1) + ids = np.asarray(self.row_ids) + if ( + ids.shape != residual.shape + or weight.shape != residual.shape + or ids.dtype.kind not in "iu" + or len(np.unique(ids)) != len(ids) + or np.any(weight < 0) + ): + raise ValueError("aligned unique row IDs, residuals and nonnegative weights required") + object.__setattr__(self, "row_ids", _array(ids, ids.dtype)) + object.__setattr__(self, "residual", residual) + object.__setattr__(self, "weight", weight) + + +@dataclass(frozen=True, eq=False) +class ResidualContext: + problem: Problem + residual: np.ndarray + + def __post_init__(self): + residual = _owned(self.residual, ndim=1) + if not isinstance(self.problem, Problem) or len(residual) != len(self.problem.target): + raise ValueError("residuals must align with a problem") + object.__setattr__(self, "residual", residual) + + def view(self, positions): + rows = np.asarray(positions) + if ( + rows.ndim != 1 + or not rows.size + or rows.dtype.kind not in "iu" + or np.any(rows < 0) + or np.any(rows >= len(self.residual)) + or len(np.unique(rows)) != len(rows) + ): + raise ValueError("nonempty unique routed row positions required") + return ResidualView( + self.problem.row_ids[rows], self.residual[rows], self.problem.weight[rows] + ) + + +def quantile_leaf(view, total=None, names=None, *, q=0.5, penalty=0.0, anchor=0.0): + """Minimize summed weighted pinball plus penalty*(value-anchor)**2/2. + + Zero penalty chooses the leftmost weighted quantile. Positive penalty gives + the unique optimum by scanning the monotone subgradient at breakpoints. + Additive total/names are accepted for the public routed solver callback. + """ + if ( + not np.isscalar(q) + or not np.isfinite(q) + or not 0 < q < 1 + or not np.isscalar(penalty) + or not np.isfinite(penalty) + or penalty < 0 + or not np.isscalar(anchor) + or not np.isfinite(anchor) + or (penalty == 0 and anchor != 0) + ): + raise ValueError("q in (0,1), nonnegative penalty and a finite applicable anchor required") + positive = view.weight > 0 + if not np.any(positive): + if penalty > 0: + return float(anchor) + raise ValueError("unpenalized quantile requires positive leaf mass") + residual, weight = view.residual[positive], view.weight[positive] + order = np.argsort(residual, kind="stable") + residual, weight = residual[order], weight[order] + with np.errstate(over="raise", invalid="raise", divide="raise"): + if penalty == 0: + mass = np.cumsum(weight / weight.max()) + return float(residual[min(np.searchsorted(mass, q * mass[-1]), len(mass) - 1)]) + values, indices = np.unique(residual, return_index=True) + masses = np.add.reduceat(weight, indices) + target = q * masses.sum() + left = 0.0 + for value, mass in zip(values, masses, strict=True): + root = anchor + (target - left) / penalty + if root <= value: + return float(root) + left += mass + if left - target + penalty * (value - anchor) >= 0: + return float(value) + result = anchor + (target - left) / penalty + if not np.isfinite(result): + raise ValueError("nonfinite penalized leaf") + return float(result) diff --git a/src/openboost/multioutput.py b/src/openboost/multioutput.py new file mode 100644 index 0000000..76edccf --- /dev/null +++ b/src/openboost/multioutput.py @@ -0,0 +1,120 @@ +"""Train-only target scaling and original-unit multi-output inference.""" + +import json +from dataclasses import dataclass, replace +from pathlib import Path + +import numpy as np + +from .artifacts import Model +from .data import _identity, _owned +from .objectives import MultiSquared + + +@dataclass(frozen=True, eq=False) +class TargetScale: + mean: np.ndarray + scale: np.ndarray + constant: tuple[bool, ...] + + def __post_init__(self): + mean, scale = _owned(self.mean, ndim=1), _owned(self.scale, ndim=1) + constant = tuple(self.constant) + if ( + mean.shape != scale.shape + or np.any(scale <= 0) + or len(constant) != len(mean) + or any(type(v) is not bool for v in constant) + or any(flag and value != 1 for flag, value in zip(constant, scale, strict=True)) + ): + raise ValueError("aligned target means, positive scales and constant flags required") + object.__setattr__(self, "mean", mean) + object.__setattr__(self, "scale", scale) + object.__setattr__(self, "constant", constant) + + @classmethod + def fit(cls, train): + MultiSquared.validate(train) + with np.errstate(over="raise", invalid="raise"): + weight = (train.weight / train.weight.sum())[:, None] + supported = train.target[train.weight > 0] + flags = np.all(supported == supported[0], axis=0) + mean = np.where(flags, supported[0], np.sum(weight * train.target, axis=0)) + variance = np.sum(weight * (train.target - mean) ** 2, axis=0) + constant = tuple(bool(v) for v in flags) + scale = np.where(flags, 1.0, np.sqrt(variance)) + return cls(mean, scale, constant) + + def transform(self, problem): + MultiSquared.validate(problem) + if problem.raw_width != len(self.mean): + raise ValueError("target width differs from fitted scaling") + with np.errstate(over="raise", invalid="raise", divide="raise"): + return replace( + problem, + target=(problem.target - self.mean) / self.scale, + offset=problem.offset / self.scale, + ) + + +@dataclass(frozen=True, eq=False) +class MultiOutputModel: + model: Model + target_scale: TargetScale + + def __post_init__(self): + if ( + not isinstance(self.model, Model) + or self.model.classes is not None + or not isinstance(self.target_scale, TargetScale) + or len(self.model.base) != len(self.target_scale.mean) + ): + raise ValueError("raw regression model must match target scaling") + + def predict(self, data, *, offset=None): + with np.errstate(over="raise", invalid="raise", divide="raise"): + if offset is not None: + offset = np.asarray(offset, dtype=float) + if offset.shape != (len(data.row_ids), len(self.model.base)): + raise ValueError("aligned original-unit offsets required") + offset = offset / self.target_scale.scale + result = self.model.predict(data, offset=offset) + return _owned(result * self.target_scale.scale + self.target_scale.mean, ndim=2) + + def record(self): + return dict( + format="openboost-multioutput-v1", + model=self.model.record(), + mean=self.target_scale.mean.tolist(), + scale=self.target_scale.scale.tolist(), + constant=list(self.target_scale.constant), + ) + + @property + def identity(self): + return _identity(self.record()) + + def save(self, path): + Path(path).write_text(json.dumps(self.record(), allow_nan=False) + "\n") + + @classmethod + def load(cls, path): + def pairs(items): + result = {} + for key, value in items: + if key in result: + raise ValueError("duplicate multi-output artifact field") + result[key] = value + return result + + record = json.loads(Path(path).read_text(), object_pairs_hook=pairs) + if ( + not isinstance(record, dict) + or set(record) != {"format", "model", "mean", "scale", "constant"} + or record["format"] != "openboost-multioutput-v1" + ): + raise ValueError("unsupported multi-output artifact") + return cls( + Model.from_record(record["model"]), + TargetScale(record["mean"], record["scale"], record["constant"]), + ) diff --git a/src/openboost/objectives.py b/src/openboost/objectives.py new file mode 100644 index 0000000..8fa37d4 --- /dev/null +++ b/src/openboost/objectives.py @@ -0,0 +1,591 @@ +"""Objective geometry over raw state, with explicit weight and offset semantics.""" + +import numpy as np + +from .data import Problem, _owned +from .stats import newton + + +class Squared: + """Scalar weighted mean half-square loss; derivatives are weighted once.""" + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.classes is not None + or problem.target.shape[1] != 1 + or problem.raw_width != 1 + or bool(problem.structure) + ): + raise ValueError("squared recipe requires scalar [N, 1] targets") + + @classmethod + def base(cls, problem): + cls.validate(problem) + with np.errstate(over="raise", invalid="raise"): + return _owned( + np.sum( + (problem.target - problem.offset) + * (problem.weight / problem.weight.sum())[:, None], + axis=0, + ), + ndim=1, + ) + + @classmethod + def gradient(cls, problem, raw): + cls.validate(problem) + with np.errstate(over="raise", invalid="raise"): + return _owned((problem.with_offset(raw) - problem.target)[:, 0], ndim=1) + + @classmethod + def fields(cls, problem, raw): + return newton(problem, cls.gradient(problem, raw), np.ones(len(problem.target))) + + @classmethod + def loss(cls, problem, raw): + gradient = cls.gradient(problem, raw) + with np.errstate(over="raise", invalid="raise"): + result = float(np.dot(problem.weight / problem.weight.sum(), gradient**2 / 2)) + if not np.isfinite(result): + raise ValueError("nonfinite squared loss") + return result + + +class Normal: + """Scalar observed targets, raw (mean, log-scale), diagonal Fisher geometry. + + Minimum scale regularizes initialization only. Runtime scale/precision must + remain strictly positive and finite; invalid trials are never clipped. + """ + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.classes is not None + or problem.target.shape[1] != 1 + or problem.raw_width != 2 + or bool(problem.structure) + ): + raise ValueError("Normal requires scalar targets and raw_width=2") + + @staticmethod + def parameters(raw): + values = _owned(raw, ndim=2) + if values.shape[1] != 2: + raise ValueError("Normal raw values require mean/log-scale columns") + with np.errstate(over="raise", invalid="raise"): + scale = np.exp(values[:, 1]) + if np.any(scale <= 0) or not np.isfinite(scale).all(): + raise ValueError("Normal scale must be positive and finite") + return _owned(np.column_stack((values[:, 0], scale)), ndim=2) + + @classmethod + def base(cls, problem, *, minimum_scale=1e-6): + cls.validate(problem) + if not np.isscalar(minimum_scale) or not np.isfinite(minimum_scale) or minimum_scale <= 0: + raise ValueError("positive finite minimum_scale required") + with np.errstate(over="raise", invalid="raise", divide="raise"): + relative_precision = np.exp(-2 * problem.offset[:, 1]) + if np.any(relative_precision <= 0): + raise ValueError("Normal offset precision underflow") + mass = problem.weight / problem.weight.sum() + mean_mass = mass * relative_precision + centered = problem.target[:, 0] - problem.offset[:, 0] + mean = np.dot(mean_mass / mean_mass.sum(), centered) + variance = np.dot(mean_mass, (centered - mean) ** 2) + base = _owned([mean, np.log(max(np.sqrt(variance), minimum_scale))], ndim=1) + # Reject an unrepresentable initial predictive distribution. + cls.loss(problem, np.broadcast_to(base, problem.offset.shape)) + return base + + @classmethod + def geometry(cls, problem, raw): + """Return loss, unweighted gradient and Fisher diagonal [N, 2].""" + cls.validate(problem) + values = problem.with_offset(raw) + cls.parameters(values) + with np.errstate(over="raise", invalid="raise", divide="raise"): + precision = np.exp(-2 * values[:, 1]) + if np.any(precision <= 0): + raise ValueError("Normal precision must be positive") + residual = values[:, 0] - problem.target[:, 0] + square = residual**2 * precision + losses = values[:, 1] + square / 2 + 0.5 * np.log(2 * np.pi) + gradient = _owned(np.column_stack((residual * precision, 1 - square)), ndim=2) + fisher = _owned(np.column_stack((precision, np.full(len(values), 2.0))), ndim=2) + loss = float(np.dot(problem.weight / problem.weight.sum(), losses)) + if not np.isfinite(loss): + raise ValueError("nonfinite Normal loss") + return loss, gradient, fisher + + @classmethod + def loss(cls, problem, raw): + return cls.geometry(problem, raw)[0] + + +def diagonal_direction(gradient, metric, *, mode="natural", damping=0.0): + """Unweighted ordinary/Fisher-diagonal direction, before regression weights.""" + g, h = _owned(gradient, ndim=2), _owned(metric, ndim=2) + if g.shape != h.shape or np.any(h <= 0): + raise ValueError("aligned positive metric diagonal required") + if not np.isscalar(damping) or not np.isfinite(damping) or damping < 0: + raise ValueError("finite nonnegative damping required") + with np.errstate(over="raise", invalid="raise", divide="raise"): + if mode == "ordinary": + if damping != 0: + raise ValueError("ordinary direction does not use damping") + return _owned(-g, ndim=2) + if mode != "natural": + raise ValueError("ordinary/natural direction required") + return _owned(-g / (h + damping), ndim=2) + + +class Formula: + """Saturation a*(1-exp(-b*x)); softplus parameters and explicit structure x.""" + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.classes is not None + or problem.target.shape[1] != 1 + or problem.raw_width != 2 + or set(problem.structure) != {"x"} + or problem.structure["x"].shape != problem.target.shape + or np.any(problem.structure["x"] <= 0) + ): + raise ValueError( + "Formula requires scalar target, raw_width=2 and positive structure x [N,1]" + ) + + @staticmethod + def predict(raw, structure): + values, x = _owned(raw, ndim=2), _owned(structure, ndim=2) + if values.shape != (len(x), 2) or x.shape[1] != 1 or np.any(x <= 0): + raise ValueError("Formula requires aligned raw [N,2] and positive structure [N,1]") + with np.errstate(over="raise", invalid="raise"): + a, b = np.logaddexp(0, values).T + if np.any(a <= 0) or np.any(b <= 0): + raise ValueError("Formula parameters underflowed") + return _owned((a * -np.expm1(-b * x[:, 0]))[:, None], ndim=2) + + @classmethod + def base(cls, problem): + cls.validate(problem) + # A deterministic initializer, not an optimum of the coupled objective. + with np.errstate(over="raise", invalid="raise", divide="raise"): + a = max( + float(np.dot(problem.weight / problem.weight.sum(), problem.target[:, 0])), 1e-6 + ) + positive = np.array([a, 1.0]) + base = _owned(positive + np.log(-np.expm1(-positive)), ndim=1) + cls.loss(problem, np.broadcast_to(base, problem.offset.shape)) + return base + + @classmethod + def geometry(cls, problem, raw): + """Weighted loss, unweighted gradient and rank-one per-row GGN.""" + cls.validate(problem) + values = problem.with_offset(raw) + x = problem.structure["x"][:, 0] + prediction = cls.predict(values, problem.structure["x"])[:, 0] + with np.errstate(over="raise", invalid="raise"): + a, b = np.logaddexp(0, values).T + sigmoid = np.exp(-np.logaddexp(0, -values)) + jacobian = np.column_stack( + (sigmoid[:, 0] * -np.expm1(-b * x), sigmoid[:, 1] * a * x * np.exp(-b * x)) + ) + residual = prediction - problem.target[:, 0] + gradient = _owned(residual[:, None] * jacobian, ndim=2) + metric = _owned(np.einsum("ni,nj->nij", jacobian, jacobian), ndim=3) + loss = float(np.dot(problem.weight / problem.weight.sum(), residual**2 / 2)) + if not np.isfinite(loss): + raise ValueError("nonfinite Formula loss") + return loss, gradient, metric + + @classmethod + def loss(cls, problem, raw): + return cls.geometry(problem, raw)[0] + + +def full_direction(gradient, metric, *, damping): + """Damped SPD solve; no silent diagonal approximation or pseudoinverse. + + A relative eigenvalue check rejects numerically singular matrices even if a + floating point Cholesky implementation happens to accept them. + """ + g, h = _owned(gradient, ndim=2), _owned(metric, ndim=3) + if h.shape != (len(g), g.shape[1], g.shape[1]) or not np.array_equal(h, h.swapaxes(1, 2)): + raise ValueError("aligned symmetric full metric required") + if not np.isscalar(damping) or not np.isfinite(damping) or damping < 0: + raise ValueError("nonnegative finite damping required") + with np.errstate(over="raise", invalid="raise", divide="raise"): + system = h + damping * np.eye(g.shape[1]) + eigenvalues = np.linalg.eigvalsh(system) + if np.any(eigenvalues[:, 0] <= np.finfo(float).eps * eigenvalues[:, -1]): + raise ValueError("metric is not numerically positive definite; specify damping") + factor = np.linalg.cholesky(system) + rhs = np.linalg.solve(factor, -g[..., None]) + return _owned(np.linalg.solve(factor.swapaxes(1, 2), rhs)[..., 0], ndim=2) + + +class Binary: + """Signed-margin logistic loss with unweighted stable first/second derivatives.""" + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.raw_width != 1 + or problem.classes is None + or len(problem.classes.values) != 2 + or problem.structure + ): + raise ValueError( + "binary requires two-class encoded targets, raw_width=1 and no structure" + ) + + @classmethod + def base(cls, problem, *, clip=1e-6): + cls.validate(problem) + if not np.isscalar(clip) or not np.isfinite(clip) or not 0 < clip < 0.5 or 1 - clip == 1: + raise ValueError("representable probability clip in (0, 0.5) required") + if len(np.unique(problem.target[:, 0])) != 2: + raise ValueError("binary training requires both classes") + with np.errstate(over="raise", invalid="raise"): + mass = problem.weight / problem.weight.sum() + p = np.clip(np.dot(mass, problem.target[:, 0]), clip, 1 - clip) + # Offset-centred prior; a deterministic initializer, not an offset MLE. + return _owned([np.log(p) - np.log1p(-p) - np.dot(mass, problem.offset[:, 0])], ndim=1) + + @classmethod + def geometry(cls, problem, raw): + from .outputs import binary_probabilities + + cls.validate(problem) + values = problem.with_offset(raw) + r, y = values[:, 0], problem.target[:, 0] + probabilities = binary_probabilities(values) + tail = np.exp(-np.abs(r)) + gradient = _owned(np.where(y == 1, -probabilities[:, 0], probabilities[:, 1]), ndim=1) + curvature = _owned(tail / (1 + tail) ** 2, ndim=1) + with np.errstate(over="raise", invalid="raise"): + losses = np.logaddexp(0, np.where(y == 1, -r, r)) + loss = float(np.dot(problem.weight / problem.weight.sum(), losses)) + if not np.isfinite(loss): + raise ValueError("nonfinite binary loss") + return loss, gradient, curvature + + @classmethod + def loss(cls, problem, raw): + return cls.geometry(problem, raw)[0] + + +class Multiclass: + """Softmax likelihood with 2*p*(1-p) diagonal upper bound, not exact Hessian.""" + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.classes is None + or problem.raw_width != len(problem.classes.values) + or problem.structure + ): + raise ValueError( + "multiclass requires class schema, one raw column per class and no structure" + ) + + @classmethod + def base(cls, problem): + cls.validate(problem) + if len(np.unique(problem.target[:, 0])) != problem.raw_width: + raise ValueError("multiclass training requires every declared class") + return _owned(np.zeros(problem.raw_width), ndim=1) + + @classmethod + def geometry(cls, problem, raw): + from .outputs import softmax_probabilities + + cls.validate(problem) + values = problem.with_offset(raw) + probability = softmax_probabilities(values) + codes = problem.target[:, 0].astype(int) + with np.errstate(over="raise", invalid="raise", divide="raise"): + shifted = values - values.max(axis=1, keepdims=True) + losses = np.log(np.exp(shifted).sum(axis=1)) - shifted[np.arange(len(values)), codes] + gradient = probability.copy() + gradient[np.arange(len(values)), codes] -= 1 + bound = 2 * probability * (1 - probability) + loss = float(np.dot(problem.weight / problem.weight.sum(), losses)) + if not np.isfinite(loss): + raise ValueError("nonfinite multiclass loss") + return loss, _owned(gradient, ndim=2), _owned(bound, ndim=2) + + @classmethod + def loss(cls, problem, raw): + return cls.geometry(problem, raw)[0] + + +class Quantile: + """Pinball loss and unit pseudo-curvature for split construction.""" + + def __init__(self, q=0.5): + if not np.isscalar(q) or not np.isfinite(q) or not 0 < q < 1: + raise ValueError("q must lie strictly between zero and one") + self.q = float(q) + + validate = staticmethod(Squared.validate) + + def residuals(self, problem, raw): + self.validate(problem) + with np.errstate(over="raise", invalid="raise"): + return _owned((problem.target - problem.with_offset(raw))[:, 0], ndim=1) + + def base(self, problem): + from .leaves import ResidualContext, quantile_leaf + + residual = self.residuals(problem, np.zeros_like(problem.target)) + view = ResidualContext(problem, residual).view(np.arange(len(residual))) + return [quantile_leaf(view, q=self.q)] + + def loss(self, problem, raw): + residual = self.residuals(problem, raw) + with np.errstate(over="raise", invalid="raise"): + value = np.maximum(self.q * residual, (self.q - 1) * residual) + return float(np.dot(problem.weight / problem.weight.sum(), value)) + + def fields(self, problem, raw): + residual = self.residuals(problem, raw) + return newton(problem, (residual < 0).astype(float) - self.q, np.ones(len(residual))) + + +class Poisson: + """Count likelihood with raw log rate, explicit exposure and additive offset.""" + + def __init__(self, minimum_rate=1e-6): + if not np.isscalar(minimum_rate) or not np.isfinite(minimum_rate) or minimum_rate <= 0: + raise ValueError("positive finite minimum_rate required") + self.minimum_rate = float(minimum_rate) + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.classes is not None + or problem.target.shape[1] != 1 + or problem.raw_width != 1 + or set(problem.structure) != {"exposure"} + ): + raise ValueError("Poisson requires scalar counts and an explicit exposure role") + y, e = problem.target[:, 0], problem.structure["exposure"] + if e.shape != problem.target.shape or np.any(e <= 0): + raise ValueError("aligned positive exposure required") + if np.any(y < 0) or np.any(y != np.floor(y)): + raise ValueError("nonnegative integer counts required") + + def base(self, problem): + self.validate(problem) + positive = problem.weight > 0 + y = problem.target[:, 0] + counted = positive & (y > 0) + if not np.any(counted): + return [float(np.log(self.minimum_rate))] + + def log_sum(values): + maximum = np.max(values) + return maximum + np.log(np.exp(values - maximum).sum()) + + with np.errstate(over="raise", invalid="raise", divide="raise"): + numerator = log_sum(np.log(problem.weight[counted]) + np.log(y[counted])) + denominator = log_sum( + np.log(problem.weight[positive]) + + np.log(problem.structure["exposure"][positive, 0]) + + problem.offset[positive, 0] + ) + value = numerator - denominator + if not np.isfinite(value): + raise ValueError("nonfinite Poisson intercept") + return [float(value)] + + def geometry(self, problem, raw): + import math + + self.validate(problem) + with np.errstate(over="raise", invalid="raise"): + log_mean = problem.with_offset(raw)[:, 0] + np.log(problem.structure["exposure"][:, 0]) + mean = np.exp(log_mean) + if np.any(mean <= 0): + raise ValueError("Poisson mean underflows float64") + y = problem.target[:, 0] + losses = mean - y * log_mean + np.array([math.lgamma(v + 1) for v in y]) + gradient = mean - y + loss = float(np.dot(problem.weight / problem.weight.sum(), losses)) + if not np.isfinite(loss): + raise ValueError("nonfinite Poisson likelihood") + return loss, _owned(gradient, ndim=1), _owned(mean, ndim=1) + + def loss(self, problem, raw): + return self.geometry(problem, raw)[0] + + +class Gamma: + """Positive mean regression: loss y/exp(f)+f, with fixed unit dispersion.""" + + @staticmethod + def validate(problem): + Squared.validate(problem) + if np.any(problem.target <= 0): + raise ValueError("Gamma targets must be strictly positive") + + @classmethod + def base(cls, problem): + cls.validate(problem) + positive = problem.weight > 0 + with np.errstate(over="raise", invalid="raise", divide="raise"): + weights = problem.weight[positive] + weights = weights / weights.max() + terms = ( + np.log(weights) + np.log(problem.target[positive, 0]) - problem.offset[positive, 0] + ) + maximum = np.max(terms) + value = maximum + np.log(np.exp(terms - maximum).sum()) - np.log(weights.sum()) + return _owned([value], ndim=1) + + @classmethod + def geometry(cls, problem, raw): + cls.validate(problem) + with np.errstate(over="raise", invalid="raise"): + values = problem.with_offset(raw)[:, 0] + ratio = np.exp(np.log(problem.target[:, 0]) - values) + if np.any(ratio <= 0): + raise ValueError("Gamma target/mean ratio underflows float64") + loss = float(np.dot(problem.weight / problem.weight.sum(), ratio + values)) + if not np.isfinite(loss): + raise ValueError("nonfinite Gamma objective") + return loss, _owned(1 - ratio, ndim=1), _owned(ratio, ndim=1) + + @classmethod + def loss(cls, problem, raw): + return cls.geometry(problem, raw)[0] + + +class Tweedie: + """Nonnegative mean objective with fixed variance power strictly between 1 and 2.""" + + def __init__(self, power=1.5, minimum_mean=1e-6): + if ( + not np.isscalar(power) + or not np.isfinite(power) + or not 1 < power < 2 + or not np.isscalar(minimum_mean) + or not np.isfinite(minimum_mean) + or minimum_mean <= 0 + ): + raise ValueError("power in (1,2) and positive finite minimum_mean required") + self.power, self.minimum_mean = float(power), float(minimum_mean) + + @staticmethod + def validate(problem): + Squared.validate(problem) + if np.any(problem.target < 0): + raise ValueError("Tweedie targets must be nonnegative") + + def base(self, problem): + self.validate(problem) + positive = problem.weight > 0 + counted = positive & (problem.target[:, 0] > 0) + if not np.any(counted): + return _owned([np.log(self.minimum_mean)], ndim=1) + + def log_sum(values): + maximum = values.max() + return maximum + np.log(np.exp(values - maximum).sum()) + + with np.errstate(over="raise", invalid="raise", divide="raise"): + numerator = log_sum( + np.log(problem.weight[counted]) + + np.log(problem.target[counted, 0]) + + (1 - self.power) * problem.offset[counted, 0] + ) + denominator = log_sum( + np.log(problem.weight[positive]) + (2 - self.power) * problem.offset[positive, 0] + ) + value = numerator - denominator + return _owned([value], ndim=1) + + def geometry(self, problem, raw): + self.validate(problem) + with np.errstate(over="raise", invalid="raise"): + values = problem.with_offset(raw)[:, 0] + y = problem.target[:, 0] + a = np.exp((2 - self.power) * values) + b = np.zeros_like(y) + positive = y > 0 + b[positive] = np.exp(np.log(y[positive]) + (1 - self.power) * values[positive]) + if np.any(a <= 0) or np.any(b[positive] <= 0): + raise ValueError("Tweedie positive terms underflow float64") + losses = b / (self.power - 1) + a / (2 - self.power) + loss = float(np.dot(problem.weight / problem.weight.sum(), losses)) + gradient = a - b + curvature = (2 - self.power) * a + (self.power - 1) * b + if not np.isfinite(loss): + raise ValueError("nonfinite Tweedie objective") + return loss, _owned(gradient, ndim=1), _owned(curvature, ndim=1) + + def loss(self, problem, raw): + return self.geometry(problem, raw)[0] + + +class MultiSquared: + """Sum of output half-squared errors, averaged with original row weights.""" + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.target_kind != "numeric" + or problem.classes is not None + or problem.structure + or problem.target.shape[1] < 1 + or problem.raw_width != problem.target.shape[1] + ): + raise ValueError("numeric multi-output targets and matching raw width required") + + @classmethod + def base(cls, problem): + cls.validate(problem) + with np.errstate(over="raise", invalid="raise"): + return _owned( + np.sum( + (problem.target - problem.offset) + * (problem.weight / problem.weight.sum())[:, None], + axis=0, + ), + ndim=1, + ) + + @classmethod + def gradient(cls, problem, raw): + cls.validate(problem) + with np.errstate(over="raise", invalid="raise"): + return _owned(problem.with_offset(raw) - problem.target, ndim=2) + + @classmethod + def mse(cls, problem, raw): + error = cls.gradient(problem, raw) + with np.errstate(over="raise", invalid="raise"): + return _owned( + np.sum((problem.weight / problem.weight.sum())[:, None] * error**2, axis=0), ndim=1 + ) + + @classmethod + def loss(cls, problem, raw): + with np.errstate(over="raise", invalid="raise"): + value = float(cls.mse(problem, raw).sum() / 2) + if not np.isfinite(value): + raise ValueError("nonfinite multi-output squared loss") + return value diff --git a/src/openboost/ops.py b/src/openboost/ops.py new file mode 100644 index 0000000..5754815 --- /dev/null +++ b/src/openboost/ops.py @@ -0,0 +1,279 @@ +"""Composable scalar CPU histogram, candidate, routing and Newton operations.""" + +from dataclasses import dataclass + +import numpy as np + +from .binning import BinnedData, _array +from .data import _identity, _owned +from .stats import RowFields + + +def _rows(rows, size): + a = np.arange(size) if rows is None else np.asarray(rows) + if ( + a.ndim != 1 + or (a.size and a.dtype.kind not in "iu") + or np.any(a < 0) + or np.any(a >= size) + or len(np.unique(a)) != len(a) + ): + raise ValueError("unique in-range row positions required") + return _array(a, " best_gain or ( + gain == best_gain and best is not None and candidate.key < best.key + ): + best, best_gain = candidate, gain + return best + + +def partition(data, rows, candidate): + """Return original positional indices; row IDs remain available on data.data.""" + selected = _rows(rows, len(data.data.values)) + if candidate.data_identity != data.identity or candidate.rows_identity != _identity(selected): + raise ValueError("candidate belongs to different data or routed rows") + if candidate.kind != data.binning.feature_kinds[candidate.feature]: + raise ValueError("candidate condition kind differs from transformer") + mask = np.where( + data.missing[candidate.feature, selected], + candidate.missing_left, + data.codes[candidate.feature, selected] == candidate.threshold + if candidate.kind == "categorical" + else data.codes[candidate.feature, selected] <= candidate.threshold, + ) + return _array(selected[mask], " 0 + and np.all(candidate.left[h] > 0) + and np.all(candidate.right[h] > 0) + and np.all(candidate.left[h] >= minimum) + and np.all(candidate.right[h] >= minimum) + ) diff --git a/src/openboost/outputs.py b/src/openboost/outputs.py new file mode 100644 index 0000000..9e40aeb --- /dev/null +++ b/src/openboost/outputs.py @@ -0,0 +1,53 @@ +"""Inference-only output transforms, independent of training objectives.""" + +import numpy as np + +from .data import _owned + + +def binary_probabilities(raw): + values = _owned(raw, ndim=2) + if values.shape[1] != 1: + raise ValueError("binary probabilities require one raw logit column") + r = values[:, 0] + tail = np.exp(-np.abs(r)) + small = tail / (1 + tail) + positive = np.where(r >= 0, 1 / (1 + tail), small) + negative = np.where(r >= 0, small, 1 / (1 + tail)) + return _owned(np.column_stack((negative, positive)), ndim=2) + + +def softmax_probabilities(raw): + values = _owned(raw, ndim=2) + if values.shape[1] < 2: + raise ValueError("softmax requires at least two raw columns") + with np.errstate(over="raise", invalid="raise", divide="raise"): + shifted = values - values.max(axis=1, keepdims=True) + exp = np.exp(shifted) + return _owned(exp / exp.sum(axis=1, keepdims=True), ndim=2) + + +def poisson_mean(raw, exposure): + """Explicit unit-exposure rate and period count mean from scalar log rates.""" + values = _owned(raw, ndim=2) + e = _owned(exposure, ndim=1) + if values.shape != (len(e), 1) or np.any(e <= 0): + raise ValueError("scalar log rates and aligned positive exposure required") + with np.errstate(over="raise", invalid="raise"): + rate = np.exp(values[:, 0]) + count = np.exp(values[:, 0] + np.log(e)) + if np.any(rate <= 0) or np.any(count <= 0): + raise ValueError("Poisson means must remain positive in float64") + return {"rate": _owned(rate, ndim=1), "count_mean": _owned(count, ndim=1)} + + +def positive_mean(raw): + """Scalar log-mean transform; no dispersion or distributional calibration.""" + values = _owned(raw, ndim=2) + if values.shape[1] != 1: + raise ValueError("one scalar log-mean column required") + with np.errstate(over="raise", invalid="raise"): + mean = np.exp(values[:, 0]) + if np.any(mean <= 0): + raise ValueError("positive mean underflows float64") + return _owned(mean, ndim=1) diff --git a/src/openboost/py.typed b/src/openboost/py.typed index 7d9c7f9..e69de29 100644 --- a/src/openboost/py.typed +++ b/src/openboost/py.typed @@ -1,2 +0,0 @@ -# Marker file for PEP 561. -# The openboost package uses inline type hints. diff --git a/src/openboost/ranking.py b/src/openboost/ranking.py new file mode 100644 index 0000000..102dcf3 --- /dev/null +++ b/src/openboost/ranking.py @@ -0,0 +1,126 @@ +"""Query-local CPU ranking geometry; pair dependencies reduce into ordinary row fields.""" + +from dataclasses import dataclass + +import numpy as np + +from .data import Problem, _owned + + +@dataclass(frozen=True, eq=False) +class PairGeometry: + loss: float + gradient: np.ndarray + curvature: np.ndarray + + +@dataclass(frozen=True) +class Ranking: + """All eligible pairs per query, normalized by eligible pair count. + + Lambda delta-NDCG weights are frozen for each geometry call. They are not + differentiated. Query weights are repeated aligned roles, not row weights. + """ + + lambdas: bool = False + k: int = 10 + + def __post_init__(self): + if type(self.lambdas) is not bool or type(self.k) is not int or self.k < 1: + raise ValueError("boolean lambdas and positive integer k required") + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.classes is not None + or problem.target.shape[1] != 1 + or problem.raw_width != 1 + or "query" not in problem.structure + or not set(problem.structure) <= {"query", "query_weight"} + ): + raise ValueError("ranking requires scalar relevance and explicit query roles") + if np.any(problem.weight != 1): + raise ValueError("ranking requires query weights, not row weights") + relevance = problem.target[:, 0] + query = problem.structure["query"] + if ( + query.shape != problem.target.shape + or np.any(query != np.floor(query)) + or np.any(np.abs(query) > 2**53 - 1) + or np.any(relevance < 0) + or np.any(relevance != np.floor(relevance)) + ): + raise ValueError("integer query codes and nonnegative integer relevance required") + weights = problem.structure.get("query_weight", np.ones_like(query)) + if weights.shape != query.shape or np.any(weights < 0): + raise ValueError("aligned nonnegative query weights required") + for q in np.unique(query): + w = weights[query[:, 0] == q, 0] + if np.any(w != w[0]): + raise ValueError("query weight must be constant within each query") + if not np.any(weights > 0): + raise ValueError("at least one positive query weight required") + + def _groups(self, problem, raw): + self.validate(problem) + scores = problem.with_offset(raw)[:, 0] + query = problem.structure["query"][:, 0] + weights = problem.structure.get("query_weight", np.ones_like(problem.target))[:, 0] + with np.errstate(over="raise", invalid="raise"): + gains = np.exp2(problem.target[:, 0]) - 1 + for q in np.unique(query): + rows = np.flatnonzero(query == q) + discount = np.zeros(len(rows)) + end = min(self.k, len(rows)) + discount[:end] = 1 / np.log2(np.arange(end) + 2) + ideal = np.dot(np.sort(gains[rows])[::-1], discount) + if not np.isfinite(ideal): + raise ValueError("nonfinite ideal DCG") + order = np.lexsort((problem.row_ids[rows], -scores[rows])) + ranked_discount = np.empty(len(rows)) + ranked_discount[order] = discount + yield rows, scores[rows], gains[rows], ranked_discount, ideal, weights[rows[0]] + + def geometry(self, problem, raw): + gradient = np.zeros(len(problem.target)) + curvature = np.zeros_like(gradient) + loss = 0.0 + for rows, scores, gains, discount, ideal, weight in self._groups(problem, raw): + relevance = problem.target[rows, 0] + high, low = np.nonzero(relevance[:, None] > relevance[None, :]) + if not len(high): + continue + with np.errstate(over="raise", invalid="raise", divide="raise"): + factor = np.full(len(high), weight / len(high)) + if self.lambdas: + factor *= ( + np.abs((gains[high] - gains[low]) * (discount[high] - discount[low])) + / ideal + if ideal > 0 + else 0 + ) + difference = scores[high] - scores[low] + tail = np.exp(-np.abs(difference)) + probability = np.where(difference >= 0, tail / (1 + tail), 1 / (1 + tail)) + h = factor * tail / (1 + tail) ** 2 + g = factor * probability + loss += float(np.dot(factor, np.logaddexp(0, -difference))) + np.add.at(gradient, rows[high], -g) + np.add.at(gradient, rows[low], g) + np.add.at(curvature, rows[high], h) + np.add.at(curvature, rows[low], h) + if not np.isfinite(loss): + raise ValueError("nonfinite pair loss") + return PairGeometry(loss, _owned(gradient, ndim=1), _owned(curvature, ndim=1)) + + def score(self, problem, raw): + """One minus query-weighted mean NDCG@k; smaller is better.""" + values, weights = [], [] + for _rows, _scores, gains, discount, ideal, weight in self._groups(problem, raw): + values.append(1.0 if ideal == 0 else float(np.dot(gains, discount) / ideal)) + weights.append(weight) + weights = np.asarray(weights) + # Scale before summing to avoid overflowing a finite query-weight vector. + weights = weights / weights.max() + return float(1 - np.dot(weights / weights.sum(), values)) diff --git a/src/openboost/recipes.py b/src/openboost/recipes.py new file mode 100644 index 0000000..ab7adf1 --- /dev/null +++ b/src/openboost/recipes.py @@ -0,0 +1,1244 @@ +"""Readable CPU recipes composed from public objective, learner and state operations.""" + +from __future__ import annotations + +from dataclasses import dataclass, replace +from functools import partial + +import numpy as np + +from .artifacts import Model, TreeTerm +from .binning import prepare_training +from .objectives import ( + Binary, + Formula, + Multiclass, + Normal, + Squared, + diagonal_direction, + full_direction, +) +from .ops import ( + _nonnegative, + feasible, + newton_leaf, + score, + vector_feasible, + vector_leaf, + vector_score, +) +from .runtime import AcceptedState, initialize, preview, propose_terms, resolve +from .stats import least_squares, newton, vector_newton +from .stopping import StopState +from .tree import depthwise + + +@dataclass(frozen=True, eq=False) +class SquaredStep: + gradient: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + + +@dataclass(frozen=True) +class FitResult: + state: AcceptedState + steps: tuple[ + SquaredStep + | NormalStep + | FormulaStep + | BinaryStep + | MulticlassStep + | RankingStep + | QuantileStep + | PoissonStep + | GammaStep + | TweedieStep + | AFTStep + | MultiSquaredStep, + ..., + ] + stop: StopState + + +def squared( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + learner=None, +): + """Scalar squared boosting, returning final state and per-round evidence. + + Fixed steps always commit finite candidates. Backtracking tries alpha/2**j + up to max_trials and requires strict training loss improvement. The learner + is fitted once each round. Validation selects best_model and optional patience + stopping. Custom learner (binned_data, weighted_fields) replaces growth; tree + options then are forbidden. + """ + Squared.validate(train) + Squared.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, Squared.base(train), score=Squared.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + gradient = Squared.gradient(train, before) + loss_before = Squared.loss(train, before) + tree = learner(binned, Squared.fields(train, before)) + state, coefficients, accepted, _failures = _trials( + state, (TreeTerm(tree, [[1]]),), Squared.loss, loss_before, rate, step, max_trials + ) + steps.append( + SquaredStep( + gradient, + before, + state.train_raw, + loss_before, + Squared.loss(train, state.train_raw), + tuple(coefficients), + accepted, + ) + ) + stop = stop.observe(Squared.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +def _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, +): + if type(rounds) is not int or rounds < 0: + raise ValueError("nonnegative integer rounds required") + if type(max_depth) is not int or max_depth < 0: + raise ValueError("nonnegative integer max_depth required") + if max_leaves is not None and (type(max_leaves) is not int or not 1 <= max_leaves <= 2**30): + raise ValueError("positive bounded integer max_leaves required") + if ( + step not in ("fixed", "backtracking") + or type(max_trials) is not int + or not 1 <= max_trials <= 6 + ): + raise ValueError("fixed/backtracking step and 1..6 trials required") + rate = _nonnegative(learning_rate) + regularizer = _nonnegative(reg_lambda) + minimum = _nonnegative(min_child_h) + penalty = _nonnegative(split_penalty) + if learner is not None: + if not callable(learner) or (max_depth, max_leaves, regularizer, minimum, penalty) != ( + 2, + None, + 1.0, + 0.0, + 0.0, + ): + raise ValueError("custom learner owns growth options") + else: + learner = partial( + depthwise, + max_depth=max_depth, + max_leaves=max_leaves, + scoring=partial(score, reg_lambda=regularizer, split_penalty=penalty), + legality=partial(feasible, min_child_h=minimum), + leaf=partial(newton_leaf, reg_lambda=regularizer), + ) + return rate, learner + + +def _trials(state, terms, loss, loss_before, rate, policy, max_trials): + # Structural errors are configuration failures, never rejected search trials. + Model( + state.model.feature_names, + state.model.base, + tuple(replace(t, coefficient=0.0) for t in terms), + state.model.classes, + ) + coefficients, failures = [], [] + for trial in range(1 if policy == "fixed" else max_trials): + alpha = rate * 0.5**trial + coefficients.append(alpha) + try: + proposal = propose_terms(state, tuple(replace(t, coefficient=alpha) for t in terms)) + candidate = preview(state, proposal) + candidate_loss = loss(state.train, candidate.predict(state.train.data)) + accepted = policy == "fixed" or candidate_loss < loss_before + updated = resolve(state, proposal, accept=accepted, score=loss) + except (ValueError, FloatingPointError, OverflowError) as error: + if policy == "fixed": + raise + failures.append(type(error).__name__) + continue + failures.append(None) + if accepted: + return updated, tuple(coefficients), True, tuple(failures) + return state, tuple(coefficients), False, tuple(failures) + + +@dataclass(frozen=True, eq=False) +class NormalStep: + gradient: np.ndarray + fisher_diagonal: np.ndarray + direction: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def normal( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="backtracking", + max_trials=6, + learner=None, + mode="natural", + damping=0.0, + minimum_scale=1e-6, +): + """Joint mean/log-scale Normal updates using shared scalar learners and state. + + Geometry is computed from the accepted snapshot; both channel trees are fit + once and committed/rejected together. Natural mode uses the diagonal Fisher, + ordinary mode uses negative likelihood gradients. No ordered update is implied. + """ + Normal.validate(train) + Normal.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + # Validate mode/damping even for zero rounds. + diagonal_direction([[0, 0]], [[1, 2]], mode=mode, damping=damping) + base = Normal.base(train, minimum_scale=minimum_scale) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, base, score=Normal.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, metric = Normal.geometry(train, before) + direction = diagonal_direction(gradient, metric, mode=mode, damping=damping) + terms = tuple( + TreeTerm(learner(binned, least_squares(train, direction[:, k])), np.eye(2)[k : k + 1]) + for k in range(2) + ) + state, coefficients, accepted, failures = _trials( + state, terms, Normal.loss, loss_before, rate, step, max_trials + ) + steps.append( + NormalStep( + gradient, + metric, + direction, + before, + state.train_raw, + loss_before, + Normal.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(Normal.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class FormulaStep: + gradient: np.ndarray + metric: np.ndarray + direction: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def formula( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="backtracking", + max_trials=6, + learner=None, + damping=0.1, +): + """Joint Formula updates through full GGN directions and shared scalar trees.""" + Formula.validate(train) + Formula.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + full_direction([[0, 0]], [[[1, 0], [0, 1]]], damping=damping) + base = Formula.base(train) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, base, score=Formula.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, metric = Formula.geometry(train, before) + direction = full_direction(gradient, metric, damping=damping) + terms = tuple( + TreeTerm(learner(binned, least_squares(train, direction[:, k])), np.eye(2)[k : k + 1]) + for k in range(2) + ) + state, coefficients, accepted, failures = _trials( + state, terms, Formula.loss, loss_before, rate, step, max_trials + ) + steps.append( + FormulaStep( + gradient, + metric, + direction, + before, + state.train_raw, + loss_before, + Formula.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(Formula.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class BinaryStep: + gradient: np.ndarray + curvature: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def binary( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + learner=None, + clip=1e-6, +): + """Binary logistic boosting with persisted class order and shared transactions.""" + Binary.validate(train) + Binary.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + base = Binary.base(train, clip=clip) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, base, score=Binary.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, curvature = Binary.geometry(train, before) + tree = learner(binned, newton(train, gradient, curvature)) + state, coefficients, accepted, failures = _trials( + state, (TreeTerm(tree, [[1]]),), Binary.loss, loss_before, rate, step, max_trials + ) + steps.append( + BinaryStep( + gradient, + curvature, + before, + state.train_raw, + loss_before, + Binary.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(Binary.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class MulticlassStep: + gradient: np.ndarray + diagonal_bound: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def multiclass( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + learner=None, +): + """One joint vector tree per round from a common softmax raw snapshot.""" + Multiclass.validate(train) + Multiclass.validate(validation) + custom = learner is not None + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + if not custom: + learner = partial( + depthwise, + max_depth=max_depth, + max_leaves=max_leaves, + scoring=partial(vector_score, reg_lambda=reg_lambda, split_penalty=split_penalty), + legality=partial(vector_feasible, min_child_h=min_child_h), + leaf=partial(vector_leaf, reg_lambda=reg_lambda), + ) + base = Multiclass.base(train) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, base, score=Multiclass.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, bound = Multiclass.geometry(train, before) + tree = learner(binned, vector_newton(train, gradient, bound)) + state, coefficients, accepted, failures = _trials( + state, + (TreeTerm(tree, np.eye(train.raw_width)),), + Multiclass.loss, + loss_before, + rate, + step, + max_trials, + ) + steps.append( + MulticlassStep( + gradient, + bound, + before, + state.train_raw, + loss_before, + Multiclass.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(Multiclass.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class RankingStep: + gradient: np.ndarray + curvature: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + pair_loss: float + + +def ranking( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + lambdas=False, + k=10, + learner=None, +): + """Fixed-step ranking; validation query-weighted NDCG selects the best model.""" + from .ranking import Ranking + + objective = Ranking(lambdas=lambdas, k=k) + objective.validate(train) + objective.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + "fixed", + 1, + learner, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, [0.0], score=objective.score) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + geometry = objective.geometry(train, before) + tree = learner(binned, newton(train, geometry.gradient, geometry.curvature)) + state = resolve( + state, + propose_terms(state, (TreeTerm(tree, [[1]], rate),)), + accept=True, + score=objective.score, + ) + steps.append( + RankingStep( + geometry.gradient, + geometry.curvature, + before, + state.train_raw, + geometry.loss, + ) + ) + stop = stop.observe(objective.score(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class QuantileStep: + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def quantile( + train, + validation, + *, + context, + q=0.5, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + penalty=0.0, + anchor=0.0, + step="fixed", + max_trials=6, + grower=depthwise, +): + """Pinball splits with exact routed quantile or penalized residual leaves. + + reg_lambda affects split scoring; penalty is the distinct leaf penalty. + Acceptance/validation use unpenalized prediction pinball loss. + """ + from .leaves import ResidualContext, quantile_leaf + from .objectives import Quantile + + objective = Quantile(q) + objective.validate(train) + objective.validate(validation) + rate, _ = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + None, + ) + base = objective.base(train) + solver = partial(quantile_leaf, q=q, penalty=penalty, anchor=anchor) + # Validate leaf configuration even for zero-round runs. + solver( + ResidualContext(train, objective.residuals(train, np.zeros_like(train.target))).view( + np.arange(len(train.target)) + ) + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, base, score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before = objective.loss(train, before) + tree = grower( + binned, + objective.fields(train, before), + max_depth=max_depth, + max_leaves=max_leaves, + scoring=partial(score, reg_lambda=reg_lambda, split_penalty=split_penalty), + legality=partial(feasible, min_child_h=min_child_h), + row_leaf=solver, + leaf_context=ResidualContext(train, objective.residuals(train, before)), + ) + state, coefficients, accepted, failures = _trials( + state, + (TreeTerm(tree, [[1]]),), + objective.loss, + loss_before, + rate, + step, + max_trials, + ) + steps.append( + QuantileStep( + before, + state.train_raw, + loss_before, + objective.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class PoissonStep: + gradient: np.ndarray + curvature: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def poisson( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + minimum_rate=1e-6, + step="fixed", + max_trials=6, + learner=None, +): + """Scalar count boosting; exposure and offsets are applied once in geometry.""" + from .objectives import Poisson + + objective = Poisson(minimum_rate) + objective.validate(train) + objective.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, objective.base(train), score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, curvature = objective.geometry(train, before) + tree = learner(binned, newton(train, gradient, curvature)) + state, coefficients, accepted, failures = _trials( + state, + (TreeTerm(tree, [[1]]),), + objective.loss, + loss_before, + rate, + step, + max_trials, + ) + steps.append( + PoissonStep( + gradient, + curvature, + before, + state.train_raw, + loss_before, + objective.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class GammaStep: + gradient: np.ndarray + curvature: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def gamma( + train, + validation, + *, + context, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + learner=None, +): + """Positive Gamma mean boosting with original weights and additive log-mean offsets.""" + from .objectives import Gamma + + objective = Gamma() + objective.validate(train) + objective.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, objective.base(train), score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, curvature = objective.geometry(train, before) + tree = learner(binned, newton(train, gradient, curvature)) + state, coefficients, accepted, failures = _trials( + state, + (TreeTerm(tree, [[1]]),), + objective.loss, + loss_before, + rate, + step, + max_trials, + ) + steps.append( + GammaStep( + gradient, + curvature, + before, + state.train_raw, + loss_before, + objective.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class TweedieStep: + gradient: np.ndarray + curvature: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def tweedie( + train, + validation, + *, + context, + power=1.5, + minimum_mean=1e-6, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + learner=None, +): + """Nonnegative fixed-power Tweedie mean boosting with original weights and additive log-mean offsets.""" + from .objectives import Tweedie + + objective = Tweedie(power, minimum_mean) + objective.validate(train) + objective.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, objective.base(train), score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, curvature = objective.geometry(train, before) + tree = learner(binned, newton(train, gradient, curvature)) + state, coefficients, accepted, failures = _trials( + state, + (TreeTerm(tree, [[1]]),), + objective.loss, + loss_before, + rate, + step, + max_trials, + ) + steps.append( + TweedieStep( + gradient, + curvature, + before, + state.train_raw, + loss_before, + objective.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class AFTStep: + gradient: np.ndarray + curvature: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + loss_before: float + loss_after: float + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def aft( + train, + validation, + *, + context, + sigma=1.0, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + learner=None, +): + """Fixed-scale log-normal AFT with event/right-censored likelihood.""" + from .survival import LogNormalAFT + + objective = LogNormalAFT(sigma) + objective.validate(train) + objective.validate(validation) + rate, learner = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + learner, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, objective.base(train), score=objective.loss) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + loss_before, gradient, curvature = objective.geometry(train, before) + tree = learner(binned, newton(train, gradient, curvature)) + state, coefficients, accepted, failures = _trials( + state, + (TreeTerm(tree, [[1]]),), + objective.loss, + loss_before, + rate, + step, + max_trials, + ) + steps.append( + AFTStep( + gradient, + curvature, + before, + state.train_raw, + loss_before, + objective.loss(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) + + +@dataclass(frozen=True, eq=False) +class MultiSquaredStep: + gradient: np.ndarray + raw_before: np.ndarray + raw_after: np.ndarray + mse_before: np.ndarray + mse_after: np.ndarray + coefficients: tuple[float, ...] + accepted: bool + failures: tuple[str | None, ...] + + +def multi_squared( + train, + validation, + *, + context, + mode="shared", + projection=None, + rounds=2, + patience=None, + min_delta=0.0, + learning_rate=0.1, + bins=254, + prepared=None, + max_depth=2, + max_leaves=None, + reg_lambda=1.0, + min_child_h=0.0, + split_penalty=0.0, + step="fixed", + max_trials=6, + grower=depthwise, +): + """Independent or shared topology; all output updates commit atomically. + + projection [K,S] is a caller-declared split sketch; full K-dimensional + statistics still solve leaves. It is supported only in shared mode. + """ + from .data import _owned + from .objectives import MultiSquared + + objective = MultiSquared + objective.validate(train) + objective.validate(validation) + if mode not in ("shared", "independent") or not callable(grower): + raise ValueError("shared/independent mode and callable grower required") + if projection is not None: + projection = _owned(projection, ndim=2) + if ( + mode != "shared" + or projection.shape[0] != train.raw_width + or np.any(np.max(np.abs(projection), axis=0) == 0) + ): + raise ValueError("nonzero shared split projection columns must match output width") + rate, _ = _configuration( + rounds, + learning_rate, + max_depth, + max_leaves, + reg_lambda, + min_child_h, + split_penalty, + step, + max_trials, + None, + ) + binned = prepare_training(train.data, bins=bins, prepared=prepared) + state = initialize(context, train, validation, objective.base(train), score=objective.loss) + mapping = np.eye(train.raw_width) + stop = StopState.start(state.best_score, rounds=rounds, patience=patience, min_delta=min_delta) + steps = [] + for _ in range(rounds): + before = state.train_raw + gradient = objective.gradient(train, before) + curvature = np.ones_like(gradient) + options = dict(max_depth=max_depth, max_leaves=max_leaves) + if mode == "shared": + leaves = vector_newton(train, gradient, curvature) + with np.errstate(over="raise", invalid="raise"): + fields = ( + leaves + if projection is None + else vector_newton(train, gradient @ projection, curvature @ (projection**2)) + ) + tree = grower( + binned, + fields, + **options, + leaf_fields=leaves, + scoring=partial(vector_score, reg_lambda=reg_lambda, split_penalty=split_penalty), + legality=partial(vector_feasible, min_child_h=min_child_h), + leaf=partial(vector_leaf, reg_lambda=reg_lambda), + ) + terms = (TreeTerm(tree, mapping),) + else: + terms = tuple( + TreeTerm( + grower( + binned, + newton(train, gradient[:, k], curvature[:, k]), + **options, + scoring=partial(score, reg_lambda=reg_lambda, split_penalty=split_penalty), + legality=partial(feasible, min_child_h=min_child_h), + leaf=partial(newton_leaf, reg_lambda=reg_lambda), + ), + mapping[k : k + 1], + ) + for k in range(train.raw_width) + ) + state, coefficients, accepted, failures = _trials( + state, + terms, + objective.loss, + objective.loss(train, before), + rate, + step, + max_trials, + ) + steps.append( + MultiSquaredStep( + gradient, + before, + state.train_raw, + objective.mse(train, before), + objective.mse(train, state.train_raw), + coefficients, + accepted, + failures, + ) + ) + stop = stop.observe(objective.loss(validation, state.validation_raw)) + if stop.reason is not None: + break + return FitResult(state, tuple(steps), stop) diff --git a/src/openboost/results.py b/src/openboost/results.py new file mode 100644 index 0000000..41acf50 --- /dev/null +++ b/src/openboost/results.py @@ -0,0 +1,44 @@ +"""Structural recipe results shared by built-in and external algorithms.""" + +from typing import Protocol, runtime_checkable + +from .runtime import AcceptedState +from .stopping import StopState + + +@runtime_checkable +class RecipeResult(Protocol): + """Completed recipe result; per-round diagnostic payloads belong to authors. + + No inheritance or conversion is required. Implementations must preserve the + immutable state/input contract; this protocol is not process isolation. + """ + + @property + def state(self) -> AcceptedState: ... + + @property + def steps(self) -> tuple[object, ...]: ... + + @property + def stop(self) -> StopState: ... + + +def validate_result(result, *, context, train, validation): + """Reject missing/malformed/incomplete/foreign results without inspecting payloads.""" + if not isinstance(result, RecipeResult): + raise ValueError("recipe result requires state, steps and stop") + state, steps, stop = result.state, result.steps, result.stop + if not isinstance(state, AcceptedState) or not isinstance(stop, StopState): + raise ValueError("recipe result requires AcceptedState and StopState") + if not isinstance(steps, tuple) or len(steps) != stop.completed_rounds: + raise ValueError("recipe steps must be a tuple with one entry per completed outer round") + if stop.reason is None: + raise ValueError("recipe returned unfinished stopping state") + if ( + state.context != context + or state.train.identity != train.identity + or state.validation.identity != validation.identity + ): + raise ValueError("recipe returned foreign run state") + return result diff --git a/src/openboost/runs.py b/src/openboost/runs.py new file mode 100644 index 0000000..d0a8c85 --- /dev/null +++ b/src/openboost/runs.py @@ -0,0 +1,77 @@ +"""Explicit sequential execution of heterogeneous CPU recipes; no batching claim.""" + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field +from types import MappingProxyType + +from .binning import PreparedData +from .data import Problem +from .results import RecipeResult, validate_result +from .runtime import RunContext + + +@dataclass(frozen=True, eq=False) +class RunSpec: + context: RunContext + train: Problem + validation: Problem + recipe: Callable + options: Mapping = field(default_factory=dict) + prepared: PreparedData | None = None + + def __post_init__(self): + if ( + not isinstance(self.context, RunContext) + or not isinstance(self.train, Problem) + or not isinstance(self.validation, Problem) + or not callable(self.recipe) + or not isinstance(self.options, Mapping) + or (self.prepared is not None and not isinstance(self.prepared, PreparedData)) + ): + raise ValueError("explicit context, problems, recipe and options required") + options = dict(self.options) + if any( + not isinstance(k, str) or k in {"context", "train", "validation", "prepared"} + for k in options + ): + raise ValueError("recipe options cannot replace run identity or problems") + if any(type(v) not in (str, int, float, bool, type(None)) for v in options.values()): + raise ValueError("run options currently require immutable scalar values") + object.__setattr__(self, "options", MappingProxyType(options)) + + +@dataclass(frozen=True) +class RunOutcome: + run_id: str + result: RecipeResult | None + error_type: str | None = None + error_message: str | None = None + + +def run_many(specs, *, execution="sequential"): + """Return every run's result/error in requested order, with unique logical IDs. + + Problems may share immutable NumericData. Independent same-ID execution is the + equivalence baseline. This is not process isolation: callbacks must honor the + immutable input contract. KeyboardInterrupt/SystemExit propagate. + """ + specs = tuple(specs) + if execution != "sequential" or any(not isinstance(s, RunSpec) for s in specs): + raise ValueError("sequential RunSpec execution required") + ids = [s.context.run_id for s in specs] + if len(set(ids)) != len(ids): + raise ValueError("run IDs must be unique before execution") + outcomes = [] + for spec in specs: + try: + preparation = {} if spec.prepared is None else {"prepared": spec.prepared} + result = spec.recipe( + spec.train, spec.validation, context=spec.context, **spec.options, **preparation + ) + validate_result( + result, context=spec.context, train=spec.train, validation=spec.validation + ) + outcomes.append(RunOutcome(spec.context.run_id, result)) + except Exception as error: + outcomes.append(RunOutcome(spec.context.run_id, None, type(error).__name__, str(error))) + return tuple(outcomes) diff --git a/src/openboost/runtime.py b/src/openboost/runtime.py new file mode 100644 index 0000000..bcb2d2f --- /dev/null +++ b/src/openboost/runtime.py @@ -0,0 +1,177 @@ +"""Explicit CPU run identity and immutable B03 proposal transactions.""" + +from dataclasses import dataclass, field, replace + +import numpy as np + +from .artifacts import ConstantTerm, Model, TreeTerm +from .data import Problem, _identity, _owned + + +@dataclass(frozen=True) +class RunContext: + run_id: str + seed: int + device: str = "cpu" + + def __post_init__(self): + if not isinstance(self.run_id, str) or not self.run_id: + raise ValueError("nonempty run ID required") + if type(self.seed) is not int or self.seed < 0: + raise ValueError("nonnegative integer seed required") + if self.device != "cpu": + raise ValueError("RunContext currently supports CPU only") + + def rng(self, round_index, component, purpose): + """Fresh generator for a logical key; retries/rejection cannot consume it. + + Reusing a key intentionally reproduces the same stream. Use distinct + component/purpose labels for independent draws, never a global RNG. + """ + if type(round_index) is not int or round_index < 0: + raise ValueError("nonnegative logical round required") + if any(not isinstance(v, str) or not v for v in (component, purpose)): + raise ValueError("nonempty RNG component and purpose required") + key = _identity("run-rng-v1", self.seed, self.run_id, round_index, component, purpose) + return np.random.default_rng(int(key[:32], 16)) + + +@dataclass(frozen=True, eq=False) +class AcceptedState: + context: RunContext + train: Problem + validation: Problem + model: Model + best_model: Model + best_score: float + version: int = 0 + train_raw: np.ndarray = field(init=False) + validation_raw: np.ndarray = field(init=False) + identity: str = field(init=False) + + def __post_init__(self): + if ( + not isinstance(self.context, RunContext) + or not isinstance(self.train, Problem) + or not isinstance(self.validation, Problem) + ): + raise ValueError("explicit context and problems required") + if type(self.version) is not int or self.version < 0 or not np.isfinite(self.best_score): + raise ValueError("valid version and finite best score required") + if not isinstance(self.model, Model) or not isinstance(self.best_model, Model): + raise ValueError("state requires ensemble artifacts") + for model in (self.model, self.best_model): + if model.classes != self.train.classes or model.classes != self.validation.classes: + raise ValueError("train/validation/model class schemas differ") + if ( + model.feature_names != self.train.data.feature_names + or model.feature_names != self.validation.data.feature_names + ): + raise ValueError("train/validation/model feature schema differs") + if ( + len(model.base) != self.train.raw_width + or len(model.base) != self.validation.raw_width + ): + raise ValueError("problem and model output widths differ") + object.__setattr__(self, "train_raw", _owned(self.model.predict(self.train.data), ndim=2)) + object.__setattr__( + self, "validation_raw", _owned(self.model.predict(self.validation.data), ndim=2) + ) + object.__setattr__( + self, + "identity", + _identity( + "accepted-v1", + self.context.run_id, + self.context.seed, + self.train.identity, + self.validation.identity, + self.model.identity, + self.best_model.identity, + float(self.best_score), + self.version, + ), + ) + + +@dataclass(frozen=True) +class Proposal: + parent_identity: str + terms: tuple[ConstantTerm | TreeTerm, ...] + + def __post_init__(self): + terms = tuple(self.terms) + if ( + not isinstance(self.parent_identity, str) + or not self.parent_identity + or not terms + or any(not isinstance(t, (ConstantTerm, TreeTerm)) for t in terms) + ): + raise ValueError("valid parent and nonempty supported proposal terms required") + object.__setattr__(self, "terms", terms) + + +def initialize(context, train, validation, base, *, score): + """Initialize with a finite validation score; smaller scores are better. + + score(problem, raw) owns objective weighting/offset semantics. Runtime raw + caches exclude input offsets. Terms and best snapshots are immutable. + """ + model = Model(train.data.feature_names, base, classes=train.classes) + initial = AcceptedState(context, train, validation, model, model, 0.0) + value = float(score(validation, initial.validation_raw)) + if not np.isfinite(value): + raise ValueError("finite initial validation score required") + return replace(initial, best_score=value) + + +def propose(state, value, *, coefficient=1.0): + return propose_terms(state, (ConstantTerm(value, coefficient),)) + + +def propose_terms(state, terms): + """Propose an atomic tuple of mapped learner/constant updates.""" + proposal = Proposal(state.identity, tuple(terms)) + candidate = preview(state, proposal) + _owned(candidate.predict(state.train.data), ndim=2) + _owned(candidate.predict(state.validation.data), ndim=2) + return proposal + + +def preview(state, proposal): + """Build a candidate model without changing any accepted or best state.""" + if not isinstance(proposal, Proposal) or proposal.parent_identity != state.identity: + raise ValueError("stale or foreign proposal parent") + return Model( + state.model.feature_names, + state.model.base, + (*state.model.terms, *proposal.terms), + state.model.classes, + ) + + +def resolve(state, proposal, *, accept, score): + """Commit atomically or return the identical state on rejection. + + Acceptance is decided by caller algorithm code. It need not mean validation + improvement. Validation chooses an immutable best snapshot independently. + """ + if type(accept) is not bool: + raise ValueError("explicit boolean acceptance required") + candidate = preview(state, proposal) + if not accept: + return state + raw = _owned(candidate.predict(state.validation.data), ndim=2) + value = float(score(state.validation, raw)) + if not np.isfinite(value): + raise ValueError("finite candidate validation score required") + improved = value < state.best_score + return AcceptedState( + state.context, + state.train, + state.validation, + candidate, + candidate if improved else state.best_model, + value if improved else state.best_score, + state.version + 1, + ) diff --git a/src/openboost/stats.py b/src/openboost/stats.py new file mode 100644 index 0000000..d6ab473 --- /dev/null +++ b/src/openboost/stats.py @@ -0,0 +1,106 @@ +"""Named additive CPU row fields with explicit training-weight ownership.""" + +from dataclasses import dataclass, replace + +import numpy as np + +from .data import Problem, _owned + + +@dataclass(frozen=True, eq=False) +class RowFields: + problem_identity: str + data_identity: str + names: tuple[str, ...] + values: np.ndarray + roles: tuple[str, ...] + + def __post_init__(self): + names, roles = tuple(self.names), tuple(self.roles) + values = _owned(self.values, ndim=2) + if ( + len(names) != values.shape[1] + or len(set(names)) != len(names) + or any(not isinstance(n, str) or not n for n in names) + ): + raise ValueError("unique names must match fields") + if len(roles) != len(names) or any( + r not in ["unweighted", "training", "independent"] for r in roles + ): + raise ValueError("explicit field weight roles required") + object.__setattr__(self, "names", names) + object.__setattr__(self, "roles", roles) + object.__setattr__(self, "values", values) + + def add_independent(self, name, values): + a = np.asarray(values, dtype=float) + if a.shape != (len(self.values),): + raise ValueError("independent field must align with rows") + return RowFields( + self.problem_identity, + self.data_identity, + (*self.names, name), + np.column_stack([self.values, a]), + (*self.roles, "independent"), + ) + + +def apply_weight(fields, problem): + if ( + not isinstance(fields, RowFields) + or not isinstance(problem, Problem) + or fields.problem_identity != problem.identity + or fields.data_identity != problem.data.identity + or len(fields.values) != len(problem.weight) + ): + raise ValueError("fields must belong to this problem") + if "training" in fields.roles or "unweighted" not in fields.roles: + raise ValueError("training weight already applied or no unweighted fields") + values = fields.values.copy() + with np.errstate(over="raise", invalid="raise"): + values[:, np.array(fields.roles) == "unweighted"] *= problem.weight[:, None] + return replace( + fields, + values=values, + roles=tuple("training" if r == "unweighted" else r for r in fields.roles), + ) + + +def newton(problem, gradient, curvature): + """Scalar unweighted derivatives -> named, once-weighted G/H fields.""" + g, h = np.asarray(gradient, dtype=float), np.asarray(curvature, dtype=float) + if g.shape != (len(problem.target),) or h.shape != g.shape or np.any(h < 0): + raise ValueError("aligned scalar gradient and nonnegative curvature required") + fields = RowFields( + problem.identity, + problem.data.identity, + ("gradient", "curvature"), + np.column_stack([g, h]), + ("unweighted", "unweighted"), + ) + return apply_weight(fields, problem) + + +def least_squares(problem, direction): + """Fit an unweighted scalar direction: G=-w*z, H=w, not likelihood curvature.""" + z = np.asarray(direction, dtype=float) + return newton(problem, -z, np.ones(len(problem.target))) + + +def vector_newton(problem, gradient, curvature): + """Unweighted diagonal [N,L] geometry -> once-weighted named vector fields.""" + g, h = _owned(gradient, ndim=2), _owned(curvature, ndim=2) + if g.shape != h.shape or len(g) != len(problem.target) or np.any(h < 0): + raise ValueError("aligned vector gradients and nonnegative diagonal curvature required") + width = g.shape[1] + names = tuple(f"{kind}:{k}" for kind in ("gradient", "curvature") for k in range(width)) + return apply_weight( + RowFields( + problem.identity, + problem.data.identity, + names, + np.column_stack((g, h)), + ("unweighted",) * (2 * width), + ), + problem, + ) diff --git a/src/openboost/stopping.py b/src/openboost/stopping.py new file mode 100644 index 0000000..a4eb82c --- /dev/null +++ b/src/openboost/stopping.py @@ -0,0 +1,73 @@ +"""Public immutable validation stopping, independent of model transactions.""" + +from dataclasses import dataclass, replace +from math import isfinite +from numbers import Real + + +def _finite(value): + if isinstance(value, bool) or not isinstance(value, Real) or not isfinite(value): + raise ValueError("finite real validation score and min_delta required") + return float(value) + + +@dataclass(frozen=True) +class StopState: + rounds: int + patience: int | None + min_delta: float + reference_score: float + last_score: float + completed_rounds: int = 0 + stale_rounds: int = 0 + + def __post_init__(self): + if type(self.rounds) is not int or self.rounds < 0: + raise ValueError("nonnegative integer round budget required") + if self.patience is not None and (type(self.patience) is not int or self.patience <= 0): + raise ValueError("positive integer patience or None required") + for name in ("min_delta", "reference_score", "last_score"): + object.__setattr__(self, name, _finite(getattr(self, name))) + if self.min_delta < 0 or (self.patience is None and self.min_delta != 0): + raise ValueError("nonnegative min_delta requires enabled patience") + if ( + type(self.completed_rounds) is not int + or not 0 <= self.completed_rounds <= self.rounds + or type(self.stale_rounds) is not int + or not 0 <= self.stale_rounds <= self.completed_rounds + or (self.patience is not None and self.stale_rounds > self.patience) + ): + raise ValueError("valid completed and stale round counts required") + + @classmethod + def start(cls, initial_score, *, rounds, patience=None, min_delta=0.0): + """Initial validation consumes no round; None disables patience stopping.""" + return cls(rounds, patience, min_delta, initial_score, initial_score) + + @property + def reason(self): + """None while running; patience takes precedence over a coincident budget.""" + if self.patience is not None and self.stale_rounds >= self.patience: + return "patience" + if self.completed_rounds >= self.rounds: + return "budget" + return None + + def observe(self, score): + """Observe once per completed outer round, including full rejection. + + Smaller is better. Improvement must strictly exceed min_delta relative + to the last qualifying improvement. Trials/substeps are not observations. + This state never chooses or mutates an accepted/best model. + """ + if self.reason is not None: + raise ValueError("cannot observe a finished stop state") + score = _finite(score) + improved = self.reference_score - score > self.min_delta + return replace( + self, + reference_score=score if improved else self.reference_score, + last_score=score, + completed_rounds=self.completed_rounds + 1, + stale_rounds=0 if improved else self.stale_rounds + 1, + ) diff --git a/src/openboost/survival.py b/src/openboost/survival.py new file mode 100644 index 0000000..5b63773 --- /dev/null +++ b/src/openboost/survival.py @@ -0,0 +1,160 @@ +"""Event/right-censored fixed-scale log-normal geometry and inference.""" + +import json +import math +from dataclasses import dataclass +from pathlib import Path +from statistics import NormalDist + +import numpy as np + +from .artifacts import Model +from .data import Problem, _identity, _owned +from .outputs import positive_mean + +# Tail quadrature differs structurally from the continued-fraction test oracle. +_NODES, _WEIGHTS = np.polynomial.laguerre.laggauss(32) + + +def _scale(sigma): + if not np.isscalar(sigma) or not np.isfinite(sigma) or sigma <= 0: + raise ValueError("positive finite AFT scale required") + square = float(sigma) * float(sigma) + if not math.isfinite(square) or square == 0 or not math.isfinite(1 / square): + raise ValueError("AFT scale geometry exceeds float64") + return float(sigma) + + +def normal_tail(z): + """Log survival, inverse Mills ratio and curvature using tail quadrature.""" + z = float(z) + log_phi = -0.5 * z * z - 0.5 * math.log(2 * math.pi) + if not math.isfinite(z) or not math.isfinite(log_phi): + raise ValueError("normal tail argument exceeds float64") + if z > 8: + kernel = np.exp(-0.5 * (_NODES / z) ** 2) + integral = float(np.dot(_WEIGHTS, kernel)) + moment = float(np.dot(_WEIGHTS, _NODES * kernel)) + return log_phi + math.log(integral) - math.log(z), z / integral, moment / integral**2 + logsf = ( + math.log(math.erfc(z / math.sqrt(2)) / 2) + if z >= 0 + else math.log1p(-math.erfc(-z / math.sqrt(2)) / 2) + ) + mills = math.exp(log_phi - logsf) + return logsf, mills, mills * (mills - z) + + +@dataclass(frozen=True) +class LogNormalAFT: + sigma: float = 1.0 + + def __post_init__(self): + object.__setattr__(self, "sigma", _scale(self.sigma)) + + @staticmethod + def validate(problem): + if ( + not isinstance(problem, Problem) + or problem.target_kind != "event_right" + or problem.raw_width != 1 + or problem.structure + or problem.classes is not None + ): + raise ValueError("AFT requires event_right bounds, one raw output and no structure") + + def base(self, problem): + self.validate(problem) + # A finite starting location, not a censoring-adjusted intercept optimum. + with np.errstate(over="raise", invalid="raise"): + value = np.dot( + problem.weight / problem.weight.sum(), + np.log(problem.target[:, 0]) - problem.offset[:, 0], + ) + return _owned([value], ndim=1) + + def geometry(self, problem, raw): + self.validate(problem) + with np.errstate(over="raise", invalid="raise", divide="raise"): + log_time = np.log(problem.target[:, 0]) + z = (log_time - problem.with_offset(raw)[:, 0]) / self.sigma + event = problem.target[:, 0] == problem.target[:, 1] + loss = log_time + math.log(self.sigma) + z**2 / 2 + math.log(2 * math.pi) / 2 + g = -z / self.sigma + h = np.full(len(z), 1 / self.sigma**2) + for i in np.flatnonzero(~event): + logsf, mills, curvature = normal_tail(z[i]) + loss[i], g[i], h[i] = -logsf, -mills / self.sigma, curvature / self.sigma**2 + aggregate = float(np.dot(problem.weight / problem.weight.sum(), loss)) + if not np.isfinite(aggregate): + raise ValueError("nonfinite AFT likelihood") + return aggregate, _owned(g, ndim=1), _owned(h, ndim=1) + + def loss(self, problem, raw): + return self.geometry(problem, raw)[0] + + +@dataclass(frozen=True, eq=False) +class AFTModel: + model: Model + sigma: float = 1.0 + + def __post_init__(self): + if ( + not isinstance(self.model, Model) + or len(self.model.base) != 1 + or self.model.classes is not None + ): + raise ValueError("scalar AFT raw model required") + object.__setattr__(self, "sigma", _scale(self.sigma)) + + def predict(self, data, *, times, probabilities=(0.5,), offset=None): + times = _owned(times, ndim=1) + probabilities = _owned(probabilities, ndim=1) + if np.any(times <= 0) or np.any(probabilities <= 0) or np.any(probabilities >= 1): + raise ValueError("positive times and probabilities in (0,1) required") + raw = self.model.predict(data, offset=offset) + with np.errstate(over="raise", invalid="raise"): + z = (np.log(times)[None, :] - raw) / self.sigma + survival = np.array([math.exp(normal_tail(v)[0]) for v in z.flat]).reshape(z.shape) + quantile_z = np.array([NormalDist().inv_cdf(float(p)) for p in probabilities]) + qraw = raw + self.sigma * quantile_z[None, :] + quantiles = positive_mean(qraw.reshape(-1, 1)).reshape(qraw.shape) + mean = positive_mean(raw + self.sigma**2 / 2) + return dict( + median=positive_mean(raw), + mean=mean, + survival=_owned(survival, ndim=2), + quantile=_owned(quantiles, ndim=2), + ) + + def record(self): + return dict( + format="openboost-aft-lognormal-v1", sigma=self.sigma, model=self.model.record() + ) + + @property + def identity(self): + return _identity(self.record()) + + def save(self, path): + Path(path).write_text(json.dumps(self.record(), allow_nan=False) + "\n") + + @classmethod + def load(cls, path): + def pairs(items): + result = {} + for key, value in items: + if key in result: + raise ValueError("duplicate AFT artifact field") + result[key] = value + return result + + record = json.loads(Path(path).read_text(), object_pairs_hook=pairs) + if ( + not isinstance(record, dict) + or set(record) != {"format", "sigma", "model"} + or record["format"] != "openboost-aft-lognormal-v1" + ): + raise ValueError("unsupported AFT artifact") + return cls(Model.from_record(record["model"]), record["sigma"]) diff --git a/src/openboost/tree.py b/src/openboost/tree.py new file mode 100644 index 0000000..5571b70 --- /dev/null +++ b/src/openboost/tree.py @@ -0,0 +1,415 @@ +"""Composable CPU scalar/vector tree growth with validated mixed-feature inference state.""" + +import heapq +import json +from dataclasses import dataclass +from pathlib import Path + +import numpy as np + +from . import ops +from .binning import Binning, _array +from .data import _identity, _owned +from .leaves import ResidualContext + + +def _indices(value): + a = np.asarray(value) + if a.ndim != 1 or a.dtype.kind not in "iu" or np.any(a < -1) or np.any(a > 2**31 - 1): + raise ValueError("int32 topology vector required") + return _array(a, " 2**31 - 1 or any( + len(getattr(self, name)) != n + for name in ("feature", "threshold", "missing_left", "left", "right") + ): + raise ValueError("aligned bounded topology required") + seen, pending = set(), [0] + while pending: + i = pending.pop() + if i < 0 or i >= n or i in seen: + raise ValueError("tree has invalid children, a cycle or shared nodes") + seen.add(i) + f, t, left, right = ( + int(getattr(self, name)[i]) for name in ("feature", "threshold", "left", "right") + ) + if f == -1: + if (t, left, right) != (-1, -1, -1) or self.missing_left[i]: + raise ValueError("invalid leaf topology") + else: + if ( + f >= len(self.binning.cuts) + or t < 0 + or t + >= ( + self.binning.bin_counts[f] + if self.binning.categories[f] is None + else len(self.binning.categories[f]) + ) + ): + raise ValueError("split exceeds transformer schema") + pending.extend((right, left)) + if len(seen) != n: + raise ValueError("unreachable tree nodes") + + @property + def identity(self): + return _identity(self.record()) + + @property + def output_width(self): + return self.value.shape[1] + + def predict(self, data): + binned = self.binning.transform(data) + result = np.empty((len(data.values), self.output_width)) + pending = [(0, np.arange(len(data.values)))] + while pending: + i, rows = pending.pop() + f = self.feature[i] + if f == -1: + result[rows] = self.value[i] + else: + mask = np.where( + binned.missing[f, rows], + self.missing_left[i], + binned.codes[f, rows] == self.threshold[i] + if self.binning.categories[f] is not None + else binned.codes[f, rows] <= self.threshold[i], + ) + pending.extend(((self.left[i], rows[mask]), (self.right[i], rows[~mask]))) + return result + + def record(self): + return dict( + format="openboost-tree-v3", + feature_names=list(self.binning.feature_names), + cuts=[c.tolist() for c in self.binning.cuts], + categories=[None if c is None else list(c) for c in self.binning.categories], + **{ + name: getattr(self, name).tolist() + for name in ("feature", "threshold", "missing_left", "left", "right", "value") + }, + ) + + def save(self, path): + Path(path).write_text(json.dumps(self.record(), allow_nan=False) + "\n") + + @classmethod + def load(cls, path): + def pairs(items): + result = {} + for key, value in items: + if key in result: + raise ValueError("duplicate artifact field") + result[key] = value + return result + + record = json.loads(Path(path).read_text(), object_pairs_hook=pairs) + return cls.from_record(record) + + @classmethod + def from_record(cls, record): + """Validate a nested inference record without a filesystem round trip.""" + vectors = ("feature", "threshold", "missing_left", "left", "right", "value") + if ( + not isinstance(record, dict) + or set(record) != {"format", "feature_names", "cuts", "categories", *vectors} + or record["format"] != "openboost-tree-v3" + or any( + not isinstance(record[name], list) + for name in ("feature_names", "cuts", "categories", *vectors) + ) + or any(not isinstance(c, list) for c in record["cuts"]) + or any(c is not None and not isinstance(c, list) for c in record["categories"]) + or any(not isinstance(row, list) for row in record["value"]) + ): + raise ValueError("unsupported or corrupt tree artifact schema") + return cls( + Binning(record["feature_names"], record["cuts"], record["categories"]), + **{name: record[name] for name in vectors}, + ) + + +class _Growth: + """Local construction scratch shared by ordinary policy loops.""" + + def __init__( + self, + data, + fields, + max_depth, + max_leaves, + scoring, + legality, + leaf, + leaf_fields, + row_leaf, + leaf_context, + ): + if type(max_depth) is not int or max_depth < 0: + raise ValueError("nonnegative integer max_depth required") + if max_leaves is None: + max_leaves = len(data.data.values) + if type(max_leaves) is not int or not 1 <= max_leaves <= 2**30: + raise ValueError("positive bounded integer max_leaves required") + if (row_leaf is None) != (leaf_context is None): + raise ValueError("routed leaf solver and context must be supplied together") + if row_leaf is not None and ( + leaf is not ops.newton_leaf + or not callable(row_leaf) + or not isinstance(leaf_context, ResidualContext) + or leaf_context.problem.identity != fields.problem_identity + ): + raise ValueError( + "routed leaf context must match problem; additive leaf must be default" + ) + self.row_leaf, self.leaf_context = row_leaf, leaf_context + self.data, self.fields = data, fields + self.leaf_fields = fields if leaf_fields is None else leaf_fields + if self.leaf_fields.problem_identity != fields.problem_identity: + raise ValueError("split and leaf fields must belong to the same problem") + ops.histogram(data, fields, []) # Validate split identity even for a root-only tree. + self.max_depth, self.max_leaves = max_depth, max_leaves + self.scoring, self.legality, self.leaf = scoring, legality, leaf + self.nodes, self.memberships, self.depths = [], [], [] + self.append(None, 0) + + def append(self, rows, depth): + hist = ops.histogram(self.data, self.leaf_fields, rows) + value = np.atleast_1d( + np.asarray( + self.leaf(hist.total, self.leaf_fields.names) + if self.row_leaf is None + else self.row_leaf( + self.leaf_context.view(hist.rows), hist.total, self.leaf_fields.names + ), + dtype=float, + ) + ) + if value.ndim != 1 or not value.size or not np.isfinite(value).all(): + raise ValueError("nonfinite or invalid custom leaf") + value = _owned(value, ndim=1) + if self.nodes and value.shape != self.nodes[0][-1].shape: + raise ValueError("inconsistent learner output width") + self.nodes.append([-1, -1, False, -1, -1, value]) + self.memberships.append(hist.rows) + self.depths.append(depth) + return len(self.nodes) - 1 + + def options(self, node): + return ops.candidates(ops.histogram(self.data, self.fields, self.memberships[node])) + + def best(self, node): + if self.depths[node] >= self.max_depth: + return None + gains = {} + + def cached(candidate): + gain = float(self.scoring(candidate)) + gains[candidate.key] = gain + return gain + + candidate = ops.choose(self.options(node), scoring=cached, legality=self.legality) + return None if candidate is None else (candidate, gains[candidate.key]) + + def split(self, node, candidate): + left_rows, right_rows = ops.partition(self.data, self.memberships[node], candidate) + if not len(left_rows) or not len(right_rows): + raise ValueError("custom legality admitted an empty child") + left = self.append(left_rows, self.depths[node] + 1) + right = self.append(right_rows, self.depths[node] + 1) + self.nodes[node][:5] = [ + candidate.feature, + candidate.threshold, + candidate.missing_left, + left, + right, + ] + return left, right + + def finish(self): + return Tree(self.data.binning, *zip(*self.nodes, strict=True)) + + +def depthwise( + data, + fields, + *, + max_depth=2, + max_leaves=None, + scoring=ops.score, + legality=ops.feasible, + leaf=ops.newton_leaf, + leaf_fields=None, + row_leaf=None, + leaf_context=None, +): + """Layer growth; highest-gain splits win a binding within-layer leaf budget.""" + work = _Growth( + data, + fields, + max_depth, + max_leaves, + scoring, + legality, + leaf, + leaf_fields, + row_leaf, + leaf_context, + ) + frontier, leaves = [0], 1 + while frontier and leaves < work.max_leaves: + choices = [] + for node in frontier: + best = work.best(node) + if best is not None: + candidate, gain = best + choices.append((node, candidate, gain)) + chosen = sorted(choices, key=lambda c: (-c[2], c[0], c[1].key))[: work.max_leaves - leaves] + frontier = [] + for node, candidate, _gain in sorted(chosen, key=lambda c: c[0]): + frontier.extend(work.split(node, candidate)) + leaves += 1 + return work.finish() + + +def best_first( + data, + fields, + *, + max_depth=2, + max_leaves=None, + scoring=ops.score, + legality=ops.feasible, + leaf=ops.newton_leaf, + leaf_fields=None, + row_leaf=None, + leaf_context=None, +): + """Heap of leaf gains; unchanged leaves retain their evaluated candidates. + + Ties use node ID then condition. Pure scoring/legality callbacks must depend + on their candidate and immutable configuration, not an evolving call count. + """ + work = _Growth( + data, + fields, + max_depth, + max_leaves, + scoring, + legality, + leaf, + leaf_fields, + row_leaf, + leaf_context, + ) + pending = [] + + def enqueue(node): + best = work.best(node) + if best is not None: + candidate, gain = best + heapq.heappush(pending, (-gain, node, candidate.key, candidate)) + + leaves = 1 + if leaves < work.max_leaves: + enqueue(0) + while pending and leaves < work.max_leaves: + _gain, node, _key, candidate = heapq.heappop(pending) + children = work.split(node, candidate) + leaves += 1 + if leaves < work.max_leaves: + for child in children: + enqueue(child) + return work.finish() + + +def symmetric( + data, + fields, + *, + max_depth=2, + max_leaves=None, + scoring=ops.score, + legality=ops.feasible, + leaf=ops.newton_leaf, + leaf_fields=None, + row_leaf=None, + leaf_context=None, +): + """One common condition per complete layer, legal in every active leaf. + + Sum gains for each common condition, including negative per-node gains. + Split only for a positive total and a budget permitting the entire layer. + """ + work = _Growth( + data, + fields, + max_depth, + max_leaves, + scoring, + legality, + leaf, + leaf_fields, + row_leaf, + leaf_context, + ) + frontier = [0] + for _ in range(max_depth): + if 2 * len(frontier) > work.max_leaves: + break + by_node = [] + for node in frontier: + candidates = {} + for candidate in work.options(node): + if legality(candidate): + gain = float(scoring(candidate)) + if not np.isfinite(gain): + raise ValueError("nonfinite custom split score") + candidates[candidate.key] = (candidate, gain) + by_node.append(candidates) + common = set.intersection(*(set(options) for options in by_node)) + if not common: + break + totals = {key: sum(options[key][1] for options in by_node) for key in common} + if not all(np.isfinite(value) for value in totals.values()): + raise ValueError("nonfinite symmetric layer score") + key = min(common, key=lambda k: (-totals[k], k)) + if totals[key] <= 0: + break + next_frontier = [] + for node, options in zip(frontier, by_node, strict=True): + next_frontier.extend(work.split(node, options[key][0])) + frontier = next_frontier + return work.finish() diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..ca9cf05 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,13 @@ +# Test scope during the v1 rebuild + +`tests/v1/` is the active suite. Its reference modules run without importing +production algorithms. The root conftest no longer loads the old OpenBoost. + +All other test files and directories are historical evidence for the retired +implementation. They are excluded from default discovery, not marked as passing +or skipped. Reproduce them at revision `50acfc6`, which also has the original +conftest. Extract useful mathematical cases into independent v1 checks as each +required capability is implemented; do not copy obsolete API expectations. + +The current 55 tests do not establish production parity, full v1 coverage, +GPU execution, predictive quality or adoption. See `v1-sprints/` for progress. diff --git a/tests/check_experimental_wheel_inference.py b/tests/check_experimental_wheel_inference.py new file mode 100644 index 0000000..dc49d71 --- /dev/null +++ b/tests/check_experimental_wheel_inference.py @@ -0,0 +1,52 @@ +"""Generate source fixtures, then verify raw inference with an isolated wheel. + +Create: python -m tests.check_experimental_wheel_inference create /tmp/ob-inference +Verify: python -I /absolute/path/to/this_file.py verify /tmp/ob-inference +Run the second command with uv --isolated --with /absolute/path/to/wheel.whl. +""" + +import argparse +import sys +from pathlib import Path + +import numpy as np + +import openboost +from openboost.experimental import Booster, TrainerConfig + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('mode', choices=['create', 'verify']) + parser.add_argument('directory', type=Path) + args = parser.parse_args() + directory = args.directory + if args.mode == 'create': + # These training-only fixtures deliberately cannot be pickled. + from tests.test_experimental_persistence import ( + NoPickleBuilder, + NoPickleObjective, + NoPickleSchedule, + ) + + directory.mkdir(parents=True, exist_ok=True) + X = np.zeros((4, 1), dtype=np.float32) + model = Booster(objective=NoPickleObjective(), tree_builder=NoPickleBuilder(), + step_schedule=NoPickleSchedule(), + config=TrainerConfig(n_trees=3, learning_rate=.5)).fit(X, np.ones(4)) + model.save(directory / 'model.ob') + np.savez(directory / 'expected.npz', X=X, **model.predict_raw(X)) + return + assert 'site-packages' in openboost.__file__, openboost.__file__ + model = Booster.load(directory / 'model.ob') + with np.load(directory / 'expected.npz') as data: + for k, v in model.predict_raw(data['X']).items(): + np.testing.assert_array_equal(v, data[k]) + assert not any(k.startswith('tests.test_experimental') for k in sys.modules) + assert model.objective is model.tree_builder is model.step_schedule is None + print('PASS: isolated wheel; plugin-free CPU raw inference; exact two-channel prediction') + print(openboost.__file__) + + +if __name__ == '__main__': + main() diff --git a/tests/conftest.py b/tests/conftest.py index 13bf9a4..1816ea2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,188 +1,15 @@ -"""Shared test fixtures for OpenBoost. +"""Collect the new v1 suite; historical tests remain as evidence, not current CI. -Centralizes dataset generation, pre-binned arrays, and gradient fixtures -to eliminate duplication across test files. All data fixtures use explicit -RandomState objects (not np.random.seed) to avoid cross-test contamination. +The original conftest and production fixtures are available at revision +50acfc6. Run historical tests in that checkout, not against the new namespace. """ -import numpy as np -import pytest +from pathlib import Path -import openboost as ob - -# ============================================================================= -# CUDA detection and auto-skip -# ============================================================================= - -try: - from numba import cuda - CUDA_AVAILABLE = cuda.is_available() -except Exception: - CUDA_AVAILABLE = False - - -def pytest_collection_modifyitems(config, items): - """Auto-skip GPU and parity tests when CUDA is unavailable.""" - if not CUDA_AVAILABLE: - skip_gpu = pytest.mark.skip(reason="CUDA not available") - for item in items: - if "gpu" in item.keywords or "parity" in item.keywords: - item.add_marker(skip_gpu) - - -# ============================================================================= -# Regression datasets -# ============================================================================= - -@pytest.fixture(scope="session") -def regression_100x5(): - """Small regression dataset: 100 samples, 5 features, linear target. - - y = X[:,0] + 0.5 * X[:,1] + noise(0.1) - """ - rng = np.random.RandomState(42) - X = rng.randn(100, 5).astype(np.float32) - y = (X[:, 0] + 0.5 * X[:, 1] + rng.randn(100).astype(np.float32) * 0.1).astype(np.float32) - return X, y - - -@pytest.fixture(scope="session") -def regression_200x10(): - """Medium regression dataset: 200 samples, 10 features, linear target. - - y = X[:,0] + 0.5 * X[:,1] - 0.3 * X[:,2] + noise(0.1) - """ - rng = np.random.RandomState(42) - X = rng.randn(200, 10).astype(np.float32) - y = (X[:, 0] + 0.5 * X[:, 1] - 0.3 * X[:, 2] + rng.randn(200).astype(np.float32) * 0.1).astype(np.float32) - return X, y - - -@pytest.fixture(scope="session") -def regression_500x10(): - """Larger regression dataset: 500 samples, 10 features. - - y = X[:,0] + 0.5 * X[:,1] - 0.3 * X[:,2] + noise(0.1) - """ - rng = np.random.RandomState(42) - X = rng.randn(500, 10).astype(np.float32) - y = (X[:, 0] + 0.5 * X[:, 1] - 0.3 * X[:, 2] + rng.randn(500).astype(np.float32) * 0.1).astype(np.float32) - return X, y - - -# ============================================================================= -# Classification datasets -# ============================================================================= - -@pytest.fixture(scope="session") -def binary_500x10(): - """Binary classification dataset: 500 samples, 10 features. - - Labels derived from a linear boundary on first two features. - """ - rng = np.random.RandomState(42) - X = rng.randn(500, 10).astype(np.float32) - logits = X[:, 0] + 0.5 * X[:, 1] - 0.3 * X[:, 2] - y = (logits > 0).astype(np.float32) - return X, y - - -@pytest.fixture(scope="session") -def multiclass_300x5(): - """3-class classification dataset: 300 samples, 5 features.""" - rng = np.random.RandomState(42) - X = rng.randn(300, 5).astype(np.float32) - # 3 classes based on which of 3 linear combos is largest - scores = np.column_stack([X[:, 0], X[:, 1], X[:, 2]]) - y = scores.argmax(axis=1).astype(np.float32) - return X, y - - -# ============================================================================= -# Specialized datasets -# ============================================================================= - -@pytest.fixture(scope="session") -def count_data_200x5(): - """Poisson count data: 200 samples, 5 features. - - y ~ Poisson(exp(0.5 * X[:,0] + 0.3 * X[:,1])) - """ - rng = np.random.RandomState(42) - X = rng.randn(200, 5).astype(np.float32) - rate = np.exp(0.5 * X[:, 0] + 0.3 * X[:, 1]) - y = rng.poisson(rate).astype(np.float32) - return X, y - - -@pytest.fixture(scope="session") -def positive_continuous_200x5(): - """Positive continuous data for Gamma/Tweedie: 200 samples, 5 features. - - y = exp(0.5 * X[:,0] + 0.3 * X[:,1]) + noise - """ - rng = np.random.RandomState(42) - X = rng.randn(200, 5).astype(np.float32) - y = (np.exp(0.5 * X[:, 0] + 0.3 * X[:, 1]) + rng.exponential(0.1, 200)).astype(np.float32) - return X, y - - -# ============================================================================= -# Pre-binned datasets -# ============================================================================= - -@pytest.fixture(scope="session") -def binned_100x5(regression_100x5): - """Pre-binned version of regression_100x5.""" - X, y = regression_100x5 - return ob.array(X), y - - -@pytest.fixture(scope="session") -def binned_200x10(regression_200x10): - """Pre-binned version of regression_200x10.""" - X, y = regression_200x10 - return ob.array(X), y - - -# ============================================================================= -# Gradient/hessian fixtures -# ============================================================================= - -@pytest.fixture -def mse_grads_100(regression_100x5): - """MSE gradients from zero predictions for regression_100x5. - - Returns (grad, hess) with grad = 2*(0-y) = -2y, hess = 2. - """ - _, y = regression_100x5 - pred = np.zeros(100, dtype=np.float32) - grad = (2 * (pred - y)).astype(np.float32) - hess = np.ones(100, dtype=np.float32) * 2 - return grad, hess - - -@pytest.fixture -def mse_grads_200(regression_200x10): - """MSE gradients from zero predictions for regression_200x10.""" - _, y = regression_200x10 - pred = np.zeros(200, dtype=np.float32) - grad = (2 * (pred - y)).astype(np.float32) - hess = np.ones(200, dtype=np.float32) * 2 - return grad, hess - - -# ============================================================================= -# Pre-fitted model fixtures -# ============================================================================= - -@pytest.fixture -def fitted_regressor(regression_500x10): - """Pre-fitted OpenBoostRegressor (20 trees, max_depth=4). - - Function-scoped: fresh model for each test. - """ - X, y = regression_500x10 - model = ob.GradientBoosting(n_trees=20, max_depth=4, learning_rate=0.1) - model.fit(X, y) - return model, X, y +_ROOT = Path(__file__).parent +collect_ignore = [ + path.name + for path in _ROOT.iterdir() + if path.name != "v1" + and (path.is_dir() or (path.name.startswith("test_") and path.suffix == ".py")) +] diff --git a/tests/foundation/check_extension_inference.py b/tests/foundation/check_extension_inference.py new file mode 100644 index 0000000..a2cfb03 --- /dev/null +++ b/tests/foundation/check_extension_inference.py @@ -0,0 +1,24 @@ +"""A new interpreter after uninstall must infer without training extensions.""" + +import importlib.util +import json +import warnings +from pathlib import Path + +import numpy as np + +from openboost.experimental import Booster + +for name in ("normal_fisher", "bounded_leaves"): + assert importlib.util.find_spec(name) is None, name +count = 0 +for path in sorted(Path("gpu_saved").glob("*.ob")): + expected = np.load(path.with_suffix(".npz")) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + model = Booster.load(path) + for channel, actual in model.predict_raw(expected["X"]).items(): + np.testing.assert_array_equal(actual, expected[channel]) + count += 1 +assert count == 9, count +print(json.dumps({"extensions_absent": True, "exact_cpu_roundtrips": count})) diff --git a/tests/foundation/conftest.py b/tests/foundation/conftest.py new file mode 100644 index 0000000..3c261a4 --- /dev/null +++ b/tests/foundation/conftest.py @@ -0,0 +1,13 @@ +"""Persist measured checks even if another required smoke case fails.""" + +import json +from pathlib import Path + +import pytest + + +@pytest.fixture(scope="session") +def checks(): + values = {} + yield values + Path("checks.json").write_text(json.dumps(values, indent=2) + "\n") diff --git a/tests/foundation/pytest.ini b/tests/foundation/pytest.ini new file mode 100644 index 0000000..2fbe0e5 --- /dev/null +++ b/tests/foundation/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +addopts = -q -n 0 --import-mode=importlib +junit_family = xunit2 +markers = + gpu: requires a real CUDA device; remote skips fail the result gate diff --git a/tests/foundation/test_baseline.py b/tests/foundation/test_baseline.py new file mode 100644 index 0000000..03f2120 --- /dev/null +++ b/tests/foundation/test_baseline.py @@ -0,0 +1,83 @@ +"""Fixed real-data matrix, run only after the GPU correctness boundary suite.""" + +import json +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_baseline_matrix(checks): + root = Path(__file__).resolve().parent + from dataset import describe + + source = json.loads((root / "manifest.json").read_text()) + assert describe(root / "cal_housing.tgz") == source["dataset"] + results = checks["baseline_cells"] = [] + for seed in (0, 1, 2): + for mode in ("resident", "eval"): + for backend in ("cpu", "cuda"): + with tempfile.TemporaryDirectory() as temp: + output = Path(temp) / "cell.json" + argv = [ + sys.executable, + str(root / "baseline_worker.py"), + backend, + str(seed), + mode, + str(root / "cal_housing.tgz"), + str(output), + ] + run = subprocess.run( + argv, + capture_output=True, + text=True, + timeout=150, + env={**os.environ, "NUMBA_CACHE_DIR": temp}, + ) + if run.returncode: + checks["baseline_failure"] = { + "backend": backend, + "seed": seed, + "mode": mode, + "returncode": run.returncode, + "stdout": run.stdout, + "stderr": run.stderr, + } + assert run.returncode == 0, run.stderr + result = json.loads(output.read_text()) + result["worker_stderr"] = run.stderr + results.append(result) + # Predeclared design gates, checked for each seed and mode. No threshold tuning. + for seed in (0, 1, 2): + for mode in ("resident", "eval"): + cpu, gpu = [ + next(c for c in results if (c["seed"], c["mode"], c["backend"]) == (seed, mode, b)) + for b in ("cpu", "cuda") + ] + a, b = (c["records"][1]["metrics"] for c in (cpu, gpu)) + assert abs(b["nll"] - a["nll"]) <= 0.01 * max(1, abs(a["nll"])) + assert b["crps"] <= 1.01 * a["crps"] + assert abs(b["coverage90"] - a["coverage90"]) <= 0.01 + for key in ("nll", "crps", "coverage90"): + assert np.isfinite(a[key]) and np.isfinite(b[key]) + for backend in ("cpu", "cuda"): + cells = [ + next( + c for c in results if (c["seed"], c["mode"], c["backend"]) == (seed, m, backend) + ) + for m in ("resident", "eval") + ] + for key in ("nll", "crps", "coverage90"): + np.testing.assert_allclose( + cells[0]["records"][1]["metrics"][key], + cells[1]["records"][1]["metrics"][key], + rtol=1e-4, + atol=1e-5, + ) diff --git a/tests/foundation/test_boundaries.py b/tests/foundation/test_boundaries.py new file mode 100644 index 0000000..37f311b --- /dev/null +++ b/tests/foundation/test_boundaries.py @@ -0,0 +1,145 @@ +"""Real-device execution boundaries for the existing unified trainer.""" + +import warnings + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def fixture_data(): + X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0) + y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + 5 * X[:, 0] + return X, y + + +@pytest.mark.parametrize('mode', ['custom', 'exposure', 'generic']) +def test_visible_fallback(mode, checks): + import openboost as ob + from openboost._distributions import Normal as BuiltinNormal + + class Normal(BuiltinNormal): + def natural_gradient(self, y, params): + result = super().natural_gradient(y, params) + g, h = result['loc'] + result['loc'] = (g * 0.5, h) + return result + + X, y = fixture_data() + kwargs, extra = {}, {} + if mode == 'custom': + kwargs['distribution'] = Normal() + elif mode == 'exposure': + kwargs['distribution'] = 'poisson' + extra['exposure'] = np.tile(np.array([1, 2], dtype=np.float32), 32) + else: + kwargs['reg_alpha'] = 0.2 + predictions = {} + for backend in ('cpu', 'cuda'): + with ob.backend_context(backend), warnings.catch_warnings(record=True) as caught: + warnings.simplefilter('always') + model = ob.NaturalBoost(n_trees=3, max_depth=1, **kwargs).fit(X, y, **extra) + predictions[backend] = model.predict(X, **extra) + if backend == 'cuda': + messages = [str(w.message) for w in caught if 'fallback' in str(w.message)] + assert messages, 'Fallback was silent' + checks[f'fallback_{mode}'] = messages + np.testing.assert_allclose(predictions['cpu'], predictions['cuda'], rtol=2e-5, atol=2e-6) + + +def test_device_error_rolls_back(monkeypatch, checks): + import openboost as ob + import openboost._backends._cuda as kernels + + def fail(*args, **kwargs): + raise RuntimeError('deliberate kernel failure') + + monkeypatch.setattr(kernels, 'normal_step_gpu', fail) + X, y = fixture_data() + with ob.backend_context('cuda'): + model = ob.NaturalBoost(n_trees=2) + with pytest.raises(RuntimeError, match='deliberate kernel failure'): + model.fit(X, y) + assert not model.trees_ and model.X_binned_ is None + with pytest.raises(RuntimeError, match='not fitted'): + model.predict(X) + checks['device_error_propagated'] = True + + +@pytest.mark.parametrize('parameter', ['subsample', 'colsample_bytree']) +def test_device_sampling_preflight(parameter, checks): + import openboost as ob + + X, y = fixture_data() + with ob.backend_context('cuda'): + model = ob.NaturalBoost(**{parameter: 0.5}) + with pytest.raises(ValueError, match='sampling'): + model.fit(X, y) + assert not model.trees_ and model.X_binned_ is None + checks[f'preflight_{parameter}'] = True + + +@pytest.mark.parametrize('distribution', ['normal', 'poisson']) +def test_eval_callback_persistence(distribution, monkeypatch, tmp_path, checks): + import openboost as ob + import openboost._trainer as trainer + from openboost._callbacks import Callback + + X, y = fixture_data() + host_raw_calls = [] + original = trainer._as_host_raw + + def host_raw(raw): + assert all(hasattr(v, '__cuda_array_interface__') for v in raw.values()) + result = original(raw) + assert all(isinstance(v, np.ndarray) for v in result.values()) + host_raw_calls.append(len(result)) + return result + + class Recorder(Callback): + def __init__(self): + self.values = [] + + def on_round_end(self, state): + assert np.isfinite(state.train_loss) and np.isfinite(state.val_loss) + self.values.append((state.train_loss, state.val_loss)) + return True + + recorder = Recorder() + with ob.backend_context('cuda'): + with monkeypatch.context() as patch: + patch.setattr(trainer, '_as_host_raw', host_raw) + plain = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1).fit(X, y) + assert host_raw_calls == [], 'Training raw scores downloaded without callback/eval' + evaluated = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1).fit( + X, y, callbacks=[recorder], eval_set=[(X, y)]) + assert len(host_raw_calls) == 3 + expected = plain.predict_params(X) + actual = evaluated.predict_params(X) + for k in expected: + np.testing.assert_allclose(actual[k], expected[k], rtol=2e-5, atol=2e-6) + assert len(recorder.values) == 3 + np.testing.assert_allclose(recorder.values[-1], evaluated.nll(X, y), rtol=2e-5) + gpu_path = tmp_path / 'gpu.ob' + evaluated.save(gpu_path) + with ob.backend_context('cpu'): + with pytest.warns(UserWarning, match='pickle'): + loaded = ob.load(gpu_path) + for k, v in loaded.predict_params(X).items(): + np.testing.assert_allclose(v, expected[k], rtol=2e-5, atol=2e-6) + cpu_model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1).fit(X, y) + cpu_expected = cpu_model.predict_params(X) + for k in expected: + np.testing.assert_allclose(cpu_expected[k], expected[k], rtol=2e-5, atol=2e-6) + cpu_path = tmp_path / 'cpu.ob' + cpu_model.save(cpu_path) + with ob.backend_context('cuda'): + with pytest.warns(UserWarning, match='pickle'): + loaded = ob.load(cpu_path) + for k, v in loaded.predict_params(X).items(): + np.testing.assert_allclose(v, cpu_expected[k], rtol=2e-5, atol=2e-6) + checks[f'boundaries_{distribution}'] = { + 'callback_rounds': 3, 'raw_host_calls_with_callback': len(host_raw_calls), + 'bidirectional_persistence': True, 'eval_final_nll': recorder.values[-1][1], + } diff --git a/tests/foundation/test_builder.py b/tests/foundation/test_builder.py new file mode 100644 index 0000000..994596f --- /dev/null +++ b/tests/foundation/test_builder.py @@ -0,0 +1,197 @@ +"""Whole-tree GPU builder parity and two-channel distribution composition.""" + +import gc +import hashlib +from dataclasses import replace + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_levelwise_builder_device_oracle(checks, monkeypatch, tmp_path): + import builder_oracle as reference + import cupy as cp + import leaf_oracle + from numba.cuda.cudadrv.devicearray import DeviceNDArray + from scipy.special import ndtr + + import openboost as ob + from openboost.experimental import Booster, LevelWiseBuilder, TrainerConfig + + copy_to_host = cp.asnumpy + transfers = [] + + def build(builder, binned, g, h, cfg, ctx): + slots = 2 ** (cfg.max_depth + 1) - 1 + + def compact_only(a, *args, **kwargs): + assert a.shape == (slots,) and a.dtype in (cp.int32, cp.float32) + transfers.append(a.nbytes) + return copy_to_host(a, *args, **kwargs) + + def forbidden(*args, **kwargs): + raise AssertionError("Numba host download in device builder") + + with monkeypatch.context() as m: + m.setattr(cp, "asnumpy", compact_only) + m.setattr(DeviceNDArray, "copy_to_host", forbidden) + result = builder.build(binned, g, h, config=cfg, context=ctx) + assert isinstance(result.train_prediction, cp.ndarray) + return result + + host, g, h = reference.example() + cfg = TrainerConfig(max_depth=2) + dg, dh = cp.asarray(g).view(), cp.asarray(h).view() + device = replace(host, data=cp.asarray(host.data).view(), device="cuda") + result = build(LevelWiseBuilder(), device, dg, dh, cfg, reference.context(cp)) + expected, prediction = reference.oracle_tree(host.data, g, h, cfg) + for k, value in expected.items(): + np.testing.assert_allclose(getattr(result.tree, k), value, atol=1e-6, rtol=1e-6) + del dg, dh, device + gc.collect() + cp.get_default_memory_pool().free_all_blocks() + np.testing.assert_allclose(copy_to_host(result.train_prediction), prediction, atol=1e-6) + with ob.backend_context("cpu"): + np.testing.assert_array_equal(result.tree(host), copy_to_host(result.train_prediction)) + device = replace(host, data=cp.asarray(host.data), device="cuda") + with cp.cuda.Stream(non_blocking=True), pytest.raises(ValueError, match="default CUDA stream"): + LevelWiseBuilder().build( + device, cp.asarray(g), cp.asarray(h), config=cfg, context=reference.context(cp) + ) + for field, value in [("reg_alpha", 1.0), ("subsample", 0.5), ("colsample_bytree", 0.5)]: + with pytest.raises(ValueError): + LevelWiseBuilder().build( + device, + cp.asarray(g), + cp.asarray(h), + config=replace(cfg, **{field: value}), + context=reference.context(cp), + ) + with pytest.raises(MemoryError): + LevelWiseBuilder(memory_budget_bytes=0).build( + device, cp.asarray(g), cp.asarray(h), config=cfg, context=reference.context(cp) + ) + device.data[0, 0] = 255 + with pytest.raises(ValueError, match="missing"): + LevelWiseBuilder().build( + device, cp.asarray(g), cp.asarray(h), config=cfg, context=reference.context(cp) + ) + device.data[0, 0] = 0 + device.is_categorical = np.array([True, False]) + with pytest.raises(ValueError, match="categorical"): + LevelWiseBuilder().build( + device, cp.asarray(g), cp.asarray(h), config=cfg, context=reference.context(cp) + ) + + def metrics(raw, y): + mu, logs = raw["mu"], raw["log_sigma"] + sigma = np.exp(logs.astype(np.float64)) + z = (y - mu) / sigma + nll = np.mean(logs + 0.5 * z * z + 0.5 * np.log(2 * np.pi)) + crps = np.mean( + sigma + * ( + z * (2 * ndtr(z) - 1) + + 2 * np.exp(-0.5 * z * z) / np.sqrt(2 * np.pi) + - 1 / np.sqrt(np.pi) + ) + ) + return {"nll": float(nll), "crps": float(crps)} + + records = [] + for samples in (16, 4097): + rng = np.random.default_rng(127) + X = rng.normal(size=(samples, 3)).astype(np.float32) + y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=samples)).astype(np.float32) + weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), samples) + with ob.backend_context("cpu"): + cpu_bins = ob.array(X, n_bins=32) + cfg = TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.2, n_bins=32) + default_output = None + for clipped in (False, True): + results = [] + for xp in (np, cp): + rule = leaf_oracle.BoundedLeaf() if clipped else None + builder = LevelWiseBuilder(leaf_rule=rule) + bins = ( + cpu_bins + if xp is np + else replace(cpu_bins, data=cp.asarray(cpu_bins.data), device="cuda") + ) + target, w = xp.asarray(y), xp.asarray(weights) + raw = {k: xp.zeros(samples, xp.float32) for k in ("mu", "log_sigma")} + # Assemble persistence state explicitly; this is NOT Booster.fit on CUDA. + model = Booster(objective=None) + model.trees_ = {k: [] for k in raw} + model.coefficients_ = {k: [] for k in raw} + model._base_scores = dict.fromkeys(raw, 0.0) + model.learning_rate, model.n_bins, model.X_binned_ = 0.2, 32, cpu_bins + for round_idx in range(2): + residual = raw["mu"] - target + inv_var = xp.exp(-2 * raw["log_sigma"]) + gradients = { + "mu": (residual * inv_var * w, inv_var * w), + "log_sigma": ((1 - residual * residual * inv_var) * w, 2 * w), + } + for channel in raw: + grad, hess = gradients[channel] + ctx = reference.context(xp, round_idx, channel) + tree = ( + builder.build(bins, grad, hess, config=cfg, context=ctx) + if xp is np + else build(builder, bins, grad, hess, cfg, ctx) + ) + coefficient = (0.2 if channel == "mu" else 0.1) / (round_idx + 1) + raw[channel] += coefficient * tree.train_prediction + model.trees_[channel].append(tree.tree) + model.coefficients_[channel].append(coefficient) + output = {k: v.copy() if xp is np else copy_to_host(v) for k, v in raw.items()} + path = tmp_path / f"model-{samples}-{clipped}-{xp.__name__}.ob" + model.save(path) + with pytest.warns(UserWarning, match="trusted"), ob.backend_context("cpu"): + restored = Booster.load(path) + inferred = restored.predict_raw(X) + for channel in raw: + np.testing.assert_allclose( + inferred[channel], output[channel], atol=2e-6, rtol=2e-5 + ) + results.append(output) + for channel in results[0]: + np.testing.assert_allclose( + results[1][channel], results[0][channel], atol=2e-5, rtol=2e-5 + ) + if not clipped: + default_output = results[0] + else: + assert any(not np.allclose(results[0][k], default_output[k]) for k in results[0]) + cpu_metrics, gpu_metrics = metrics(results[0], y), metrics(results[1], y) + for name in cpu_metrics: + np.testing.assert_allclose( + gpu_metrics[name], cpu_metrics[name], atol=2e-5, rtol=2e-5 + ) + records.append( + { + "samples": samples, + "seed": 127, + "clipped": clipped, + "data_sha256": hashlib.sha256( + X.tobytes() + y.tobytes() + weights.tobytes() + ).hexdigest(), + "cpu": cpu_metrics, + "cuda": gpu_metrics, + "max_raw_error": max( + float(np.max(np.abs(results[1][k] - results[0][k]))) for k in results[0] + ), + } + ) + assert len(transfers) == 5 * 17 # one oracle tree plus 4 cells * 2 rounds * 2 channels + checks["levelwise_builder"] = { + "device_cache_survives_owner_release": True, + "compact_transfer_calls": len(transfers), + "compact_transfer_bytes": sum(transfers), + "two_channel_cases": records, + "cpu_load_prediction": True, + "scope": "direct GPU builder composition; GPU Booster.fit remains P5; named transfers only", + } diff --git a/tests/foundation/test_correctness.py b/tests/foundation/test_correctness.py new file mode 100644 index 0000000..421f776 --- /dev/null +++ b/tests/foundation/test_correctness.py @@ -0,0 +1,117 @@ +"""Real CUDA weighted Newton regression, using fixed bins and host oracles.""" + +from types import SimpleNamespace + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_weighted_newton(checks, monkeypatch): + from numba import cuda + + import openboost as ob + import openboost._trainer as trainer + from openboost._array import BinnedArray + from openboost._backends._cuda import _build_histogram_shared_kernel + from openboost._core._histogram import build_histogram + + bins = np.array([[0, 0, 0, 0, 1, 1, 1, 1]], dtype=np.uint8) + weights = np.array([0, 1, 2, 4, 0, 2, 3, 5], dtype=np.float32) + direction = np.array([-3, -3, -3, -3, 2, 2, 2, 2], dtype=np.float32) + grad, hess = direction * weights, weights.copy() + expected_leaves = np.array([21 / 8, -20 / 11], dtype=np.float32) + expected_raw = 0.1 * np.repeat(expected_leaves, 4) + binned = BinnedArray(bins, [np.array([0.5])], 1, 8, 'cpu') + + class FixedObjective: + channel_names = ['value'] + device_capable = False + unit_hessian = True + + def init_raw(self, *args): + return {'value': 0.0} + + def step(self, raw, y, sample_weight, extra): + return {'value': (direction * sample_weight, sample_weight.copy())} + + with ob.backend_context('cpu'): + hist_cpu = build_histogram(bins, grad, hess) + cpu = SimpleNamespace() + trainer.fit_boosting(cpu, FixedObjective(), binned, direction, + config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights) + raw_cpu = trainer.predict_raw(cpu, binned)['value'] + original = trainer.fit_tree_gpu_native + captured = {} + + def native(x, g, h, **kwargs): + hist = cuda.to_device(np.zeros((1, 1, 256, 2), dtype=np.float32)) + nodes = cuda.to_device(np.zeros(8, dtype=np.int32)) + _build_histogram_shared_kernel[(1, 1), 256]( + x, g, h, nodes, 0, 1, 0, hist, np.float32(kwargs['const_hess'])) + captured['hist'] = hist.copy_to_host()[0, 0] + return original(x, g, h, **kwargs) + + monkeypatch.setattr(trainer, 'fit_tree_gpu_native', native) + with ob.backend_context('cuda'): + gpu = SimpleNamespace() + trainer.fit_boosting(gpu, FixedObjective(), binned, direction, + config=trainer.TrainerConfig(n_trees=1, max_depth=1), sample_weight=weights) + raw_gpu = trainer.predict_raw(gpu, binned)['value'] + checks['weighted_newton'] = { + 'cpu_hist_hess': hist_cpu[1][0, :2].tolist(), + 'gpu_hist_hess': captured['hist'][:2, 1].tolist(), + 'expected_leaves': expected_leaves.tolist(), + 'cpu_raw': raw_cpu.tolist(), 'gpu_raw': raw_gpu.tolist(), + } + np.testing.assert_allclose(raw_cpu, expected_raw, rtol=1e-6) + np.testing.assert_allclose(captured['hist'][:, 0], hist_cpu[0][0], atol=1e-6) + np.testing.assert_allclose(captured['hist'][:, 1], hist_cpu[1][0], atol=1e-6) + np.testing.assert_allclose(raw_gpu, expected_raw, rtol=1e-6) + assert gpu.trees_['value'][0].features[0] == 0 + assert gpu.trees_['value'][0].thresholds[0] == 0 + + +@pytest.mark.parametrize('distribution', ['normal', 'poisson']) +def test_weighted_distribution(distribution, checks): + from numba import cuda + + import openboost as ob + from openboost._objectives import DistributionObjective + from openboost._trainer import predict_raw + + # Two distinct bins and no near-tied alternative splits. Reuse CPU bins. + X = np.repeat(np.array([[0.], [1.]], dtype=np.float32), 32, axis=0) + y = np.tile(np.array([1, 2, 3, 4], dtype=np.float32), 16) + X[:, 0] * 5 + weights = np.tile(np.array([0, 1, 2, 4], dtype=np.float32), 16) + models, raws, metrics, gradients = {}, {}, {}, {} + with ob.backend_context('cpu'): + binned = ob.array(X) + for backend in ('cpu', 'cuda'): + with ob.backend_context(backend): + model = ob.NaturalBoost(distribution=distribution, n_trees=3, max_depth=1) + model.fit(binned, y, sample_weight=weights) + models[backend] = model + raws[backend] = predict_raw(model, binned) + metrics[backend] = float(model.nll(binned, y)) + objective = DistributionObjective(model.distribution_, natural=True) + raw = {k: np.full(len(y), v, dtype=np.float32) for k, v in model._base_scores.items()} + if backend == 'cuda': + output = objective.step({k: cuda.to_device(v) for k, v in raw.items()}, + cuda.to_device(y), cuda.to_device(weights)) + gradients[backend] = {k: tuple(a.copy_to_host() for a in pair) for k, pair in output.items()} + else: + gradients[backend] = objective.step(raw, y, weights) + checks[f'weighted_{distribution}'] = { + 'nll': metrics, + 'raw_max_abs_error': max(float(np.max(np.abs(raws['cpu'][k] - raws['cuda'][k]))) for k in raws['cpu']), + } + for channel in raws['cpu']: + for a, b in zip(gradients['cpu'][channel], gradients['cuda'][channel], strict=True): + np.testing.assert_allclose(a, b, rtol=2e-5, atol=2e-6) + np.testing.assert_allclose(raws['cpu'][channel], raws['cuda'][channel], rtol=2e-5, atol=2e-6) + for a, b in zip(models['cpu'].trees_[channel], models['cuda'].trees_[channel], strict=True): + assert a.features[0] == b.features[0] + assert a.thresholds[0] == b.thresholds[0] + np.testing.assert_allclose(metrics['cpu'], metrics['cuda'], rtol=2e-5) diff --git a/tests/foundation/test_extensions.py b/tests/foundation/test_extensions.py new file mode 100644 index 0000000..e8d18e4 --- /dev/null +++ b/tests/foundation/test_extensions.py @@ -0,0 +1,237 @@ +"""Installed independent packages, real GPU math and full shared-trainer fits.""" + +import hashlib +import importlib.metadata +import json +import subprocess +import sys +import zipfile +from pathlib import Path + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_installed_gpu_extensions(checks, monkeypatch): + import bounded_leaves + import cupy as cp + import normal_fisher + from bounded_leaves import BoundedNewton + from normal_fisher import ChannelDecay, NormalFisher + from scipy.special import ndtr + + from openboost.experimental import Booster, ExecutionContext, LevelWiseBuilder, TrainerConfig + + manifest = json.loads(Path("manifest.json").read_text()) + installed = {} + for module, distname in [ + (normal_fisher, "openboost-example-normal-fisher"), + (bounded_leaves, "openboost-example-bounded-leaves"), + ]: + location = Path(module.__file__) + assert "site-packages" in location.parts + dist = importlib.metadata.distribution(distname) + wheel = next( + n + for n in manifest["extension_wheels"] + if n.startswith(distname.replace("-", "_") + "-") + ) + with zipfile.ZipFile(wheel) as archive: + count = 0 + for name in archive.namelist(): + if name.endswith(".py"): + assert archive.read(name) == Path(dist.locate_file(name)).read_bytes() + count += 1 + assert count > 0 + installed[module.__name__] = { + "version": dist.version, + "verified_python_files": count, + "path": str(location.relative_to(sys.prefix)), + } + obj = NormalFisher() + ctx = ExecutionContext("cuda", cp, np.random.default_rng(7), 0) + y = np.array([-1, 2, 4], np.float32) + w = np.array([0, 0.5, 2], np.float32) + raw = { + "mu": np.array([0.2, 0.4, 0.1], np.float32), + "log_sigma": np.array([-0.3, 0.2, 0.5], np.float32), + } + stats = obj.step( + {k: cp.asarray(v) for k, v in raw.items()}, cp.asarray(y), cp.asarray(w), context=ctx + ) + + def nll(values): + return w * ( + values["log_sigma"] + + 0.5 * ((y - values["mu"]) * np.exp(-values["log_sigma"])) ** 2 + + 0.5 * np.log(2 * np.pi) + ) + + for k in raw: + plus, minus = ({j: a.astype(float) for j, a in raw.items()} for _ in range(2)) + plus[k] += 1e-5 + minus[k] -= 1e-5 + np.testing.assert_allclose( + cp.asnumpy(stats[k][0]), (nll(plus) - nll(minus)) / 2e-5, atol=1e-6, rtol=2e-6 + ) + assert all(isinstance(a, cp.ndarray) and a.dtype == cp.float32 for a in stats[k]) + np.testing.assert_allclose( + cp.asnumpy(stats["mu"][1]), w * np.exp(-2 * raw["log_sigma"]), rtol=1e-6 + ) + np.testing.assert_array_equal(cp.asnumpy(stats["log_sigma"][1]), 2 * w) + gpu_raw = {k: cp.asarray(v) for k, v in raw.items()} + np.testing.assert_allclose( + obj.loss_value(gpu_raw, cp.asarray(y), cp.asarray(w), context=ctx), + nll({k: v.astype(float) for k, v in raw.items()}).sum() / w.sum(), + ) + constrained = obj.constrain(gpu_raw) + assert isinstance(constrained["sigma"], cp.ndarray) + np.testing.assert_allclose( + cp.asnumpy(constrained["sigma"]), np.exp(raw["log_sigma"].astype(float)) + ) + with pytest.raises(ValueError, match="device"): + obj.step(raw, y, w, context=ctx) + for logs in (-1000, 1000): + with pytest.raises(ValueError): + obj.step( + {"mu": cp.ones(2, cp.float32), "log_sigma": cp.full(2, logs, cp.float32)}, + cp.ones(2, cp.float32), + context=ctx, + ) + + class RecordedNormal(NormalFisher): + def __init__(self): + self.gradients = [] + + def step(self, *args, **kwargs): + out = super().step(*args, **kwargs) + self.gradients.append(out["mu"][0].copy()) + return out + + copy_to_host = cp.asnumpy + downloads = [] + records = [] + saved = Path("gpu_saved") + saved.mkdir(exist_ok=True) + for samples in (16, 4097): + rng = np.random.default_rng(149) + X = rng.normal(size=(samples, 3)).astype(np.float32) + y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=samples)).astype(np.float32) + weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), samples) + outputs, next_gradients = {}, {} + cfg = TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.2, n_bins=32, random_state=7) + for bounded, scheduled in [(False, False), (False, True), (True, False), (True, True)]: + result = [] + for device in ("cpu", "cuda"): + objective = RecordedNormal() + model = Booster( + objective=objective, + tree_builder=LevelWiseBuilder( + leaf_rule=BoundedNewton(0.1) if bounded else None + ), + step_schedule=ChannelDecay() if scheduled else None, + config=cfg, + device=device, + ) + + def compact_only(a, *args, **kwargs): + assert a.shape == (7,) and a.dtype in (cp.int32, cp.float32) + downloads.append(a.nbytes) + return copy_to_host(a, *args, **kwargs) + + with monkeypatch.context() as m: + if device == "cuda": + m.setattr(cp, "asnumpy", compact_only) + model.fit(X, y, sample_weight=weights) + prediction = model.predict_raw(X) + result.append(prediction) + if device == "cuda": + assert model.fit_report_["actual_device"] == "cuda" + assert model.fit_report_["fallback_reason"] is None + if scheduled: + assert model.coefficients_ == {"mu": [0.2, 0.1], "log_sigma": [0.1, 0.05]} + if bounded: + assert all( + np.max(np.abs(tree.values)) <= 0.100001 + for trees in model.trees_.values() + for tree in trees + ) + next_gradients[bounded, scheduled] = copy_to_host(objective.gradients[1]) + outputs[bounded, scheduled] = prediction + path = saved / f"{samples}-{bounded}-{scheduled}.ob" + model.save(path) + np.savez(path.with_suffix(".npz"), X=X, **prediction) + for channel in result[0]: + np.testing.assert_allclose( + result[1][channel], result[0][channel], atol=2e-5, rtol=2e-5 + ) + + def metrics(pred, y=y, weights=weights): + sigma = np.exp(pred["log_sigma"].astype(float)) + z = (y - pred["mu"]) / sigma + return { + "nll": float( + np.average( + pred["log_sigma"] + 0.5 * z * z + 0.5 * np.log(2 * np.pi), + weights=weights, + ) + ), + "crps": float( + np.average( + sigma + * ( + z * (2 * ndtr(z) - 1) + + 2 * np.exp(-z * z / 2) / np.sqrt(2 * np.pi) + - 1 / np.sqrt(np.pi) + ), + weights=weights, + ) + ), + } + + cpu_metrics, gpu_metrics = metrics(result[0]), metrics(result[1]) + for metric in cpu_metrics: + np.testing.assert_allclose( + gpu_metrics[metric], cpu_metrics[metric], atol=2e-5, rtol=2e-5 + ) + records.append( + { + "samples": samples, + "seed": 149, + "bounded": bounded, + "scheduled": scheduled, + "data_sha256": hashlib.sha256( + X.tobytes() + y.tobytes() + weights.tobytes() + ).hexdigest(), + "cpu": cpu_metrics, + "cuda": gpu_metrics, + "max_raw_error": max( + float(np.max(np.abs(result[0][k] - result[1][k]))) for k in result[0] + ), + } + ) + assert not np.allclose(outputs[False, False]["mu"], outputs[False, True]["mu"]) + assert not np.allclose(outputs[False, True]["mu"], outputs[True, True]["mu"]) + assert not np.allclose(next_gradients[False, True], next_gradients[True, True]) + assert len(downloads) == 160 + checks["installed_gpu_extensions"] = { + "installed": installed, + "math_oracle": True, + "cases": records, + "clipping_changes_next_gradient": True, + "schedule_changes_prediction": True, + "compact_transfer_calls": len(downloads), + "compact_transfer_bytes": sum(downloads), + "models_saved": 8, + } + demo = subprocess.run( + [sys.executable, "extension_demo.py", "--device", "cuda"], + text=True, + capture_output=True, + ) + assert demo.returncode == 0, demo.stdout + demo.stderr + for suffix in ("ob", "npz"): + Path("demo." + suffix).rename(saved / ("demo." + suffix)) + checks["installed_gpu_extensions"].update(demo=json.loads(demo.stdout), models_saved=9) diff --git a/tests/foundation/test_histograms.py b/tests/foundation/test_histograms.py new file mode 100644 index 0000000..095820f --- /dev/null +++ b/tests/foundation/test_histograms.py @@ -0,0 +1,75 @@ +"""Real CUDA batch histogram parity against direct sample sums.""" + +import hashlib + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_batch_histogram_device_oracle(checks, monkeypatch): + import cupy as cp + import histogram_oracle as reference + from numba.cuda.cudadrv.devicearray import DeviceNDArray + + from openboost._core import _primitives as legacy + from openboost.experimental import build_histograms + + rng = np.random.default_rng(103) + bins = rng.integers(0, 256, (3, 4097), dtype=np.uint8) + weights = rng.choice(np.array([0, .5, 1, 3], np.float32), 4097) + grad = rng.normal(size=4097).astype(np.float32) * weights + hess = rng.uniform(.1, 2, 4097).astype(np.float32) * weights + random = (bins, grad, hess, rng.integers(-1, 7, 4097, dtype=np.int32), + np.array([True, False, True, True, False, True, True])) + empty = (np.zeros((2, 0), np.uint8), np.zeros(0, np.float32), np.zeros(0, np.float32), + np.zeros(0, np.int32), np.ones(3, bool)) + records = [] + + def forbidden(*args, **kwargs): + raise AssertionError('Full host download or legacy histogram wrapper was called') + + for host in (reference.fixture(), random, empty): + wanted = reference.oracle(*host) + cpu = build_histograms(*host) + device = tuple(cp.asarray(a) for a in host) + # A non-default stream also exercises producer/kernel/output ordering. + with cp.cuda.Stream(non_blocking=True) as stream: + # Synchronize initial input copies before handing them to this stream. + cp.cuda.Stream.null.synchronize() + with monkeypatch.context() as m: + m.setattr(cp, 'asnumpy', forbidden) + m.setattr(DeviceNDArray, 'copy_to_host', forbidden) + m.setattr(legacy, 'build_node_histograms', forbidden) + batch = build_histograms(*device) + assert all(isinstance(a, cp.ndarray) for a in (batch.grad, batch.hess, batch.counts, batch.active)) + stream.synchronize() + errors = [] + for actual, cpu_array, expected in zip((batch.grad, batch.hess, batch.counts), + (cpu.grad, cpu.hess, cpu.counts), wanted, strict=True): + result = cp.asnumpy(actual) + np.testing.assert_allclose(result, expected, rtol=2e-5, atol=2e-5) + np.testing.assert_allclose(result, cpu_array, rtol=2e-5, atol=2e-5) + errors.append(float(np.max(np.abs(result - expected)))) + np.testing.assert_array_equal(cp.asnumpy(batch.active), host[-1]) + with pytest.raises(MemoryError): + build_histograms(*device, memory_budget_bytes=batch.nbytes - 1) + records.append({'input_sha256': hashlib.sha256(b''.join(a.tobytes() for a in host)).hexdigest(), 'seed': 103 if host is random else None, 'samples': host[0].shape[1], 'features': host[0].shape[0], + 'slots': len(host[-1]), 'max_abs_errors': errors, 'bytes': batch.nbytes}) + invalid = list(device) + invalid[1] = np.zeros(0, np.float32) + with pytest.raises(TypeError): + build_histograms(*invalid) + device = tuple(cp.asarray(a) for a in reference.fixture()) + invalid = list(device) + invalid[2] = cp.full(6, -1, cp.float32) + with pytest.raises(ValueError): + build_histograms(*invalid) + invalid = list(device) + invalid[3] = cp.full(6, 99, cp.int32) + with pytest.raises(ValueError): + build_histograms(*invalid) + checks['batch_histograms'] = {'device_arrays': True, 'cases': records, + 'legacy_download_wrappers_blocked': True, + 'scope': 'named host wrappers only; scalar validation synchronization allowed; no profiler trace'} diff --git a/tests/foundation/test_leaves.py b/tests/foundation/test_leaves.py new file mode 100644 index 0000000..8ddcd4a --- /dev/null +++ b/tests/foundation/test_leaves.py @@ -0,0 +1,129 @@ +"""Real CUDA row reduction and custom leaf rule evidence.""" + +import hashlib + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_batch_leaf_rule_oracle(checks, monkeypatch): + import cupy as cp + import leaf_oracle as reference + from numba.cuda.cudadrv.devicearray import DeviceNDArray + + from openboost._core import _primitives as legacy + from openboost.experimental import TrainerConfig, leaf_values, reduce_leaves + + def forbidden(*args, **kwargs): + raise AssertionError("Full sample-array download or legacy wrapper called") + + rng = np.random.default_rng(109) + w = rng.choice(np.array([0, 0.5, 1, 3], np.float32), 4097) + random = ( + rng.integers(-5, 6, 4097).astype(np.float32) * w, + w, + rng.integers(-1, 7, 4097, dtype=np.int32), + np.array([True, False, True, True, False, True, True]), + ) + empty = ( + np.zeros(0, np.float32), + np.zeros(0, np.float32), + np.zeros(0, np.int32), + np.ones(3, bool), + ) + records = [] + for host in (reference.example(), random, empty): + device = tuple(cp.asarray(a) for a in host) + cp.cuda.Stream.null.synchronize() + with cp.cuda.Stream(non_blocking=True) as stream: + with monkeypatch.context() as m: + m.setattr(cp, "asnumpy", forbidden) + m.setattr(DeviceNDArray, "copy_to_host", forbidden) + m.setattr(legacy, "compute_leaf_values", forbidden) + stats = reduce_leaves(*device) + normal = leaf_values(*device) + bounded = leaf_values(*device, leaf_rule=reference.BoundedLeaf()) + assert all( + isinstance(a, cp.ndarray) + for a in (stats.grad, stats.hess, stats.counts, normal, bounded) + ) + stream.synchronize() + G, H, counts = reference.direct(*host) + for actual, expected in zip( + (stats.grad, stats.hess, stats.counts), (G, H, counts), strict=True + ): + np.testing.assert_array_equal(cp.asnumpy(actual), expected) + wanted = -G / (H + 1) + np.testing.assert_allclose(cp.asnumpy(normal), wanted, atol=1e-6, rtol=1e-6) + np.testing.assert_allclose( + cp.asnumpy(bounded), np.clip(wanted, -0.5, 0.5), atol=1e-6, rtol=1e-6 + ) + records.append( + { + "input_sha256": hashlib.sha256(b"".join(a.tobytes() for a in host)).hexdigest(), + "samples": len(host[0]), + "slots": len(host[-1]), + "seed": 109 if host is random else None, + "counts": counts.tolist(), + "grad": G.tolist(), + "hess": H.tolist(), + "values": cp.asnumpy(normal).tolist(), + "bounded": cp.asnumpy(bounded).tolist(), + } + ) + y, w = cp.asarray([2, 4], dtype=cp.float32), cp.asarray([1, 3], dtype=cp.float32) + ids, active = cp.zeros(2, cp.int32), cp.asarray([True]) + rounds = [] + for rule in (None, reference.BoundedLeaf()): + raw = cp.zeros(2, cp.float32) + history = [] + with monkeypatch.context() as m: + m.setattr(cp, "asnumpy", forbidden) + m.setattr(DeviceNDArray, "copy_to_host", forbidden) + for _ in range(2): + g = (raw - y) * w + history.append(g.copy()) + raw += leaf_values(g, w.copy(), ids, active, leaf_rule=rule)[ids] + rounds.append( + {"raw": cp.asnumpy(raw).tolist(), "second_gradient": cp.asnumpy(history[1]).tolist()} + ) + np.testing.assert_allclose(rounds[0]["raw"], [3.36, 3.36], atol=1e-6) + np.testing.assert_array_equal(rounds[1]["raw"], [1, 1]) + np.testing.assert_allclose(rounds[0]["second_gradient"], [0.8, -3.6], atol=1e-6) + np.testing.assert_array_equal(rounds[1]["second_gradient"], [-1.5, -10.5]) + zero = cp.zeros(2, cp.float32) + assert float(leaf_values(zero, zero, ids, active, config=TrainerConfig(reg_lambda=0))[0]) == 0 + with pytest.raises(ValueError, match="curvature"): + leaf_values(cp.ones(2, cp.float32), zero, ids, active, config=TrainerConfig(reg_lambda=0)) + + class Bad: + supported_devices = frozenset({"cuda"}) + + def __init__(self, kind): + self.kind = kind + + def values(self, g, h, *, config, context): + if self.kind == "mutation": + g[0] += 1 + if self.kind == "host": + return np.zeros(len(g), np.float32) + if self.kind == "dtype": + return cp.zeros(len(g), cp.float64) + if self.kind == "nan": + return cp.full(len(g), cp.nan, cp.float32) + return cp.ones(len(g), cp.float32) + + device = tuple(cp.asarray(a) for a in reference.example()) + for kind in ("mutation", "host", "dtype", "nan", "inactive"): + with pytest.raises((TypeError, ValueError)): + leaf_values(*device, leaf_rule=Bad(kind)) + checks["batch_leaves"] = { + "device_arrays": True, + "row_sum_oracle": True, + "bounded_changes_next_gradient": True, + "cases": records, + "two_rounds": rounds, + "scope": "GPU primitive composition; CPU trainer tested separately; not assembled GPU Booster", + } diff --git a/tests/foundation/test_smoke.py b/tests/foundation/test_smoke.py new file mode 100644 index 0000000..9e067bf --- /dev/null +++ b/tests/foundation/test_smoke.py @@ -0,0 +1,99 @@ +"""Small real-device tests, run outside the repository against an installed wheel.""" + +import gc +import hashlib +import importlib.metadata +import json +import site +import zipfile +from pathlib import Path + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_device_interop(checks): + import cupy as cp + from numba import cuda + + import openboost as ob + + assert cuda.is_available() + manifest = json.loads(Path("manifest.json").read_text()) + wheel = Path(manifest["wheel"]) + assert hashlib.sha256(wheel.read_bytes()).hexdigest() == manifest["wheel_sha256"] + module = Path(ob.__file__).resolve() + assert any(module.is_relative_to(Path(p).resolve()) for p in site.getsitepackages()) + dist = importlib.metadata.distribution("openboost") + verified = 0 + with zipfile.ZipFile(wheel) as archive: + for name in archive.namelist(): + if name.startswith("openboost/") and name.endswith(".py"): + assert Path(dist.locate_file(name)).read_bytes() == archive.read(name) + verified += 1 + checks["installed_files_verified"] = verified + checks["installed_module"] = str(module) + + owner = cp.arange(32, dtype=cp.float32) + view = cuda.as_cuda_array(owner) + assert view.__cuda_array_interface__["data"][0] == owner.data.ptr + del owner + gc.collect() + + @cuda.jit + def increment(values): + i = cuda.grid(1) + if i < values.size: + values[i] += 1 + + increment[1, 32](view) + cuda.synchronize() + np.testing.assert_array_equal(view.copy_to_host(), np.arange(32, dtype=np.float32) + 1) + checks["interop"] = True + + +def test_normal_gpu_fit(checks, monkeypatch): + from numba import cuda + + import openboost as ob + import openboost._trainer as trainer + from openboost._objectives import DistributionObjective + + rng = np.random.default_rng(31) + X = rng.normal(size=(256, 4)).astype(np.float32) + y = (X[:, 0] + 0.3 * rng.normal(size=256)).astype(np.float32) + checks["dataset_sha256"] = hashlib.sha256(X.tobytes() + y.tobytes()).hexdigest() + calls = {"objective": 0, "tree": 0} + original_step = DistributionObjective.step + original_tree = trainer.fit_tree_gpu_native + + def step(self, raw, target, *args, **kwargs): + assert all(hasattr(a, "__cuda_array_interface__") for a in raw.values()) + output = original_step(self, raw, target, *args, **kwargs) + assert self._device_kernels_ok, "Objective silently fell back to host" + assert all(hasattr(a, "__cuda_array_interface__") for pair in output.values() for a in pair) + calls["objective"] += 1 + return output + + def tree(binned, grad, hess, **kwargs): + assert all(hasattr(a, "__cuda_array_interface__") for a in (binned, grad, hess)) + output = original_tree(binned, grad, hess, **kwargs) + calls["tree"] += 1 + return output + + monkeypatch.setattr(DistributionObjective, "step", step) + monkeypatch.setattr(trainer, "fit_tree_gpu_native", tree) + with ob.backend_context("cuda"): + assert ob.is_cuda() + model = ob.NaturalBoostNormal(n_trees=2, max_depth=2).fit(X, y) + prediction = model.predict(X) + cuda.synchronize() + assert np.all(np.isfinite(prediction)) + assert all(len(trees) == 2 for trees in model.trees_.values()) + checks["nll"] = float(model.nll(X, y)) + assert np.isfinite(checks["nll"]) + checks["native_tree_calls"] = calls["tree"] + checks["device_objective_calls"] = calls["objective"] + assert calls == {"objective": 2, "tree": 4} diff --git a/tests/foundation/test_splits.py b/tests/foundation/test_splits.py new file mode 100644 index 0000000..6dd875d --- /dev/null +++ b/tests/foundation/test_splits.py @@ -0,0 +1,159 @@ +"""Real-device split/routing with exhaustive row-mask reference.""" + +import hashlib + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_batch_split_routing_oracle(checks, monkeypatch): + import cupy as cp + import split_oracle as reference + from numba.cuda.cudadrv.devicearray import DeviceNDArray + + from openboost._core import _primitives as legacy + from openboost.experimental import build_histograms, find_splits, partition + + def forbidden(*args, **kwargs): + raise AssertionError("Full-array host download or legacy wrapper used") + + records = [] + configs = [ + {}, + {"reg_lambda": 0.0, "min_child_weight": 0.0}, + {"min_gain": 1e6}, + {"min_child_weight": 20.0}, + ] + for params in configs: + host = reference.example() + host[3][-1] = -1 + bins, g, h, ids, active = host + expected = reference.exhaustive(*host, **params) + device = [cp.asarray(a) for a in host] + with monkeypatch.context() as m: + m.setattr(cp, "asnumpy", forbidden) + m.setattr(DeviceNDArray, "copy_to_host", forbidden) + m.setattr(legacy, "build_node_histograms", forbidden) + hist = build_histograms(*device) + splits = find_splits(hist, **params) + routed = partition(device[0], device[3], splits) + assert isinstance(routed, cp.ndarray) + assert all( + isinstance(getattr(splits, k), cp.ndarray) + for k in ("feature", "threshold", "left_child", "right_child", "gain", "valid") + ) + np.testing.assert_array_equal(cp.asnumpy(splits.feature), [x[0] for x in expected]) + np.testing.assert_array_equal(cp.asnumpy(splits.threshold), [x[1] for x in expected]) + np.testing.assert_allclose( + cp.asnumpy(splits.gain), [x[2] for x in expected], atol=1e-10, rtol=1e-10 + ) + wanted_ids = ids.copy() + for i, node in enumerate(ids): + if node >= 0 and expected[node][0] >= 0: + f, threshold, _ = expected[node] + wanted_ids[i] = 2 * node + (1 if bins[f, i] <= threshold else 2) + np.testing.assert_array_equal(cp.asnumpy(routed), wanted_ids) + np.testing.assert_array_equal(cp.asnumpy(device[3]), ids) + child_active = cp.zeros_like(device[4]) + child_active[splits.left_child[splits.valid]] = True + child_active[splits.right_child[splits.valid]] = True + with monkeypatch.context() as m: + m.setattr(cp, "asnumpy", forbidden) + m.setattr(DeviceNDArray, "copy_to_host", forbidden) + child = build_histograms(device[0], device[1], device[2], routed, child_active) + second = find_splits(child, **params) + active_host = cp.asnumpy(child_active) + for node in np.flatnonzero(active_host): + np.testing.assert_allclose( + cp.asnumpy(child.grad)[node, 0].sum(), g[wanted_ids == node].sum(), atol=1e-6 + ) + np.testing.assert_allclose( + cp.asnumpy(child.hess)[node, 0].sum(), h[wanted_ids == node].sum(), atol=1e-6 + ) + expected_second = reference.exhaustive(bins, g, h, wanted_ids, active_host, **params) + np.testing.assert_array_equal(cp.asnumpy(second.feature), [x[0] for x in expected_second]) + np.testing.assert_array_equal(cp.asnumpy(second.threshold), [x[1] for x in expected_second]) + records.append( + { + "parameters": params, + "input_sha256": hashlib.sha256(b"".join(a.tobytes() for a in host)).hexdigest(), + "feature": cp.asnumpy(splits.feature).tolist(), + "threshold": cp.asnumpy(splits.threshold).tolist(), + "gain": cp.asnumpy(splits.gain).tolist(), + "routed_ids": wanted_ids.tolist(), + } + ) + # Exact feature/threshold ties and min_gain equality are separate from random rounding. + b = cp.asarray([[0, 0, 2, 2], [0, 0, 2, 2]], dtype=cp.uint8) + hist = build_histograms( + b, + cp.asarray([2, 2, -2, -2], dtype=cp.float32), + cp.ones(4, cp.float32), + cp.zeros(4, cp.int32), + cp.asarray([True, False, False]), + ) + tie = find_splits(hist, reg_lambda=0.0, min_gain=16.0, min_child_weight=2.0) + assert int(tie.feature[0]) == 0 and int(tie.threshold[0]) == 0 and float(tie.gain[0]) == 16.0 + assert not bool( + find_splits(hist, reg_lambda=0.0, min_gain=np.nextafter(16.0, np.inf)).valid.any() + ) + for kind in ("constant", "zero", "terminal", "inactive"): + host = list(reference.example()) + if kind == "constant": + host[0][:] = 2 + if kind == "zero": + host[2][:] = 0 + if kind == "terminal": + host[3][:] = 6 + host[4][:] = False + host[4][6] = True + if kind == "inactive": + host[4][:] = False + device = [cp.asarray(a) for a in host] + result = find_splits(build_histograms(*device), min_child_weight=0.0) + assert not bool(result.valid.any()) + np.testing.assert_array_equal(cp.asnumpy(partition(device[0], device[3], result)), host[3]) + with pytest.raises(ValueError): + partition(b, cp.full(4, 99, cp.int32), tie) + tie.left_child[0] = 2 + with pytest.raises(ValueError): + partition(b, cp.zeros(4, cp.int32), tie) + tie.left_child[0] = 1 + empty = build_histograms( + b[:, :0].copy(), + cp.zeros(0, cp.float32), + cp.zeros(0, cp.float32), + cp.zeros(0, cp.int32), + cp.asarray([True, False, False]), + ) + assert partition(b[:, :0].copy(), cp.zeros(0, cp.int32), find_splits(empty)).size == 0 + negative = build_histograms( + b, + cp.ones(4, cp.float32), + cp.ones(4, cp.float32), + cp.zeros(4, cp.int32), + cp.asarray([True, False, False]), + ) + assert not bool(find_splits(negative).valid.any()) + b[0, 0] = 255 + with pytest.raises(ValueError, match="missing"): + partition(b, cp.zeros(4, cp.int32), tie) + with pytest.raises(ValueError, match="missing"): + find_splits( + build_histograms( + b, + cp.ones(4, cp.float32), + cp.ones(4, cp.float32), + cp.zeros(4, cp.int32), + cp.asarray([True, False, False]), + ) + ) + checks["batch_splits"] = { + "device_arrays": True, + "routed_child_oracle": True, + "cases": records, + "exact_ties_and_gain_boundary": True, + "scope": "numeric L2 positive-curvature children; no full-array named downloads; no whole-trainer claim", + } diff --git a/tests/foundation/test_trainer.py b/tests/foundation/test_trainer.py new file mode 100644 index 0000000..a930aa8 --- /dev/null +++ b/tests/foundation/test_trainer.py @@ -0,0 +1,237 @@ +"""Actual strict experimental GPU fit, independent of legacy native dispatch.""" + +import hashlib +import shutil + +import numpy as np +import pytest + +pytestmark = pytest.mark.gpu + + +def test_strict_extension_trainer(checks, monkeypatch, tmp_path): + import cupy as cp + from numba.cuda.cudadrv.devicearray import DeviceNDArray + from scipy.special import ndtr + + import openboost as ob + import openboost._trainer as trainer + from openboost.experimental import ( + Booster, + BuiltTree, + DistributionObjectiveAdapter, + ExecutionContext, + LevelWiseBuilder, + TrainerConfig, + ) + + class Decay: + def coefficients(self, round_idx, channel_names, base_learning_rate): + return { + k: base_learning_rate / (round_idx + 1) / (i + 1) + for i, k in enumerate(channel_names) + } + + rng = np.random.default_rng(137) + X = rng.normal(size=(257, 3)).astype(np.float32) + y = (1.2 * X[:, 0] + 0.4 * rng.normal(size=257)).astype(np.float32) + weights = rng.choice(np.array([0, 0.5, 1, 2], np.float32), 257) + config = TrainerConfig(n_trees=2, max_depth=2, n_bins=32, learning_rate=0.2, random_state=7) + objective = DistributionObjectiveAdapter("normal", natural=True) + with ob.backend_context("cpu"): + cpu = Booster( + objective=objective, + tree_builder=LevelWiseBuilder(), + step_schedule=Decay(), + config=config, + ).fit(X, y, sample_weight=weights) + expected = cpu.predict_raw(X) + transfers, calls = [], [] + copy_to_host = cp.asnumpy + original_build = LevelWiseBuilder.build + + def tracked(self, binned, grad, hess, *, config, context): + assert context.device == "cuda" and context.xp is cp + assert all(isinstance(a, cp.ndarray) for a in (binned.data, grad, hess)) + calls.append((context.round_idx, context.channel)) + return original_build(self, binned, grad, hess, config=config, context=context) + + def compact_only(a, *args, **kwargs): + assert a.shape == (7,) and a.dtype in (cp.int32, cp.float32) + transfers.append(a.nbytes) + return copy_to_host(a, *args, **kwargs) + + def forbidden(*args, **kwargs): + raise AssertionError("Legacy dispatch or host sample download during strict fit") + + class ExternalBuilder: + supported_devices = frozenset({"cpu", "cuda"}) + + def build(self, *args, **kwargs): + return LevelWiseBuilder().build(*args, **kwargs) + + records = [] + for explicit in (False, True): + model = Booster( + objective=objective, + tree_builder=ExternalBuilder() if explicit else None, + step_schedule=Decay(), + config=config, + device="cuda", + ) + with monkeypatch.context() as m: + m.setattr(LevelWiseBuilder, "build", tracked) + m.setattr(cp, "asnumpy", compact_only) + m.setattr(DeviceNDArray, "copy_to_host", forbidden) + m.setattr(trainer, "_to_host", forbidden) + m.setattr(trainer, "fit_tree_gpu_native", forbidden) + model.fit(X, y, sample_weight=weights) + assert model.fit_report_["actual_device"] == "cuda" + assert model.fit_report_["objective_device"] == model.fit_report_["update_device"] == "cuda" + assert model.fit_report_["fallback_reason"] is None + assert model.coefficients_ == {"loc": [0.2, 0.1], "scale": [0.1, 0.05]} + actual = model.predict_raw(X) + for k in expected: + np.testing.assert_allclose(actual[k], expected[k], atol=2e-5, rtol=2e-5) + path = tmp_path / f"model-{explicit}.ob" + model.save(path) + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(path) + for k in actual: + np.testing.assert_array_equal(loaded.predict_raw(X)[k], actual[k]) + ctx = ExecutionContext("cpu", np, np.random.default_rng(7), 2) + nll_cpu = objective.loss_value(expected, y, weights, context=ctx) + nll_gpu = objective.loss_value(actual, y, weights, context=ctx) + np.testing.assert_allclose(nll_gpu, nll_cpu, atol=2e-5, rtol=2e-5) + + def crps(raw): + params = objective.constrain(raw) + z = (y - params["loc"]) / params["scale"] + scores = params["scale"] * ( + z * (2 * ndtr(z) - 1) + + 2 * np.exp(-z * z / 2) / np.sqrt(2 * np.pi) + - 1 / np.sqrt(np.pi) + ) + return float(np.average(scores, weights=weights)) + + crps_cpu, crps_gpu = crps(expected), crps(actual) + np.testing.assert_allclose(crps_gpu, crps_cpu, atol=2e-5, rtol=2e-5) + records.append( + { + "explicit_builder": explicit, + "fit_report": model.fit_report_, + "max_raw_error": max( + float(np.max(np.abs(actual[k] - expected[k]))) for k in actual + ), + "cpu_crps": crps_cpu, + "cuda_crps": crps_gpu, + "cpu_nll": nll_cpu, + "cuda_nll": nll_gpu, + } + ) + assert calls == [(r, k) for _ in range(2) for r in range(2) for k in ("loc", "scale")] + assert len(transfers) == 40 + + class Broken(DistributionObjectiveAdapter): + def step(self, *args, **kwargs): + raise RuntimeError("broken GPU kernel") + + class WrongDevice(DistributionObjectiveAdapter): + def step(self, raw, y, *args, **kwargs): + return {k: (np.zeros(len(y), np.float32), np.ones(len(y), np.float32)) for k in raw} + + class Mutating(DistributionObjectiveAdapter): + def step(self, raw, y, *args, **kwargs): + raw["loc"][:] = 123 + return super().step(raw, y, *args, **kwargs) + + before = model.predict_raw(X) + report = model.fit_report_.copy() + for bad, error, match in [ + (Broken("normal"), RuntimeError, "broken"), + (WrongDevice("normal"), TypeError, "CuPy"), + (Mutating("normal"), ValueError, "mutated"), + ]: + model.objective = bad + with pytest.raises(error, match=match): + model.fit(X, y, sample_weight=weights) + assert model.fit_report_ == report + for k in before: + np.testing.assert_array_equal(model.predict_raw(X)[k], before[k]) + model.objective = objective + + class BadCache(LevelWiseBuilder): + def build(self, *args, **kwargs): + built = super().build(*args, **kwargs) + return BuiltTree(built.tree, cp.full_like(built.train_prediction, 123)) + + model.tree_builder, model._default_builder = BadCache(), False + with pytest.raises(ValueError, match="prediction"): + model.fit(X, y, sample_weight=weights) + np.testing.assert_array_equal(model.predict_raw(X)["loc"], before["loc"]) + # The adapter advertises Normal/Poisson, natural and ordinary gradients. + # Verify the actual shared trainer across that whole declared surface. + adapters = [] + for distribution in ("normal", "poisson"): + target = y if distribution == "normal" else rng.poisson(2, len(y)).astype(np.float32) + for natural in (False, True): + obj = DistributionObjectiveAdapter(distribution, natural=natural) + models = [ + Booster( + objective=obj, tree_builder=LevelWiseBuilder(), config=config, device=device + ).fit(X, target, sample_weight=weights) + for device in ("cpu", "cuda") + ] + outputs = [m.predict_raw(X) for m in models] + for channel in outputs[0]: + np.testing.assert_allclose( + outputs[1][channel], outputs[0][channel], atol=2e-5, rtol=2e-5 + ) + adapters.append( + { + "distribution": distribution, + "natural": natural, + "target_sha256": hashlib.sha256(target.tobytes()).hexdigest(), + "max_raw_error": max( + float(np.max(np.abs(outputs[1][k] - outputs[0][k]))) for k in outputs[0] + ), + } + ) + + class InvalidStats(DistributionObjectiveAdapter): + def __init__(self, fault): + super().__init__("normal", natural=True) + self.fault = fault + + def step(self, raw, y, *args, **kwargs): + out = super().step(raw, y, *args, **kwargs) + g, h = out["loc"] + if self.fault == "dtype": + out["loc"] = (g.astype(cp.float64), h) + elif self.fault == "negative": + out["loc"] = (g, -cp.ones_like(h)) + elif self.fault == "alias": + out["loc"] = (raw["loc"], h) + return out + + for fault, match in [("dtype", "float32"), ("negative", "nonnegative"), ("alias", "alias")]: + with pytest.raises(ValueError, match=match): + Booster(objective=InvalidStats(fault), device="cuda", config=config).fit(X, y) + + checks["strict_extension_trainer"] = { + "adapter_cases": adapters, + "additional_invalid_statistics": 3, + "actual_fit": True, + "legacy_dispatch_blocked": True, + "rollback": True, + "cpu_load_prediction": True, + "cases": records, + "compact_transfer_calls": len(transfers), + "compact_transfer_bytes": sum(transfers), + "data_sha256": hashlib.sha256(X.tobytes() + y.tobytes() + weights.tobytes()).hexdigest(), + "samples": 257, + "seed": 137, + "nsys_available": shutil.which("nsys") is not None, + "profiler_trace_collected": False, + "scope": "named transfer wrappers only; scalar syncs and device defensive copies allowed", + } diff --git a/tests/foundation/test_value.py b/tests/foundation/test_value.py new file mode 100644 index 0000000..91fce3d --- /dev/null +++ b/tests/foundation/test_value.py @@ -0,0 +1,86 @@ +"""Resident P7 matrix: a negative value result is still complete evidence.""" + +import json +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +import pytest + +pytestmark = pytest.mark.gpu + + +def test_value_matrix(checks): + from dataset import describe + from value_protocol import STRATEGIES, summarize, validate_profiles + + root = Path(__file__).resolve().parent + manifest = json.loads((root / "manifest.json").read_text()) + assert describe(root / "cal_housing.tgz") == manifest["dataset"] + frozen = json.loads((root / "p2_baseline.json").read_text())["checks"]["baseline_cells"] + cells = checks["value_cells"] = [] + checks["frozen_baseline_cells"] = frozen + checks["unsupported_comparisons"] = ["strict CUDA eval/callbacks"] + profile_only = manifest["suite"] == "value_profile" + for seed in (0,) if profile_only else (0, 1, 2): + for strategy in STRATEGIES: + with tempfile.TemporaryDirectory() as temp: + output = Path(temp) / "cell.json" + argv = [ + sys.executable, + str(root / "value_worker.py"), + strategy, + str(seed), + str(root / "cal_housing.tgz"), + str(output), + ] + if profile_only: + argv.append("--profile-only") + try: + run = subprocess.run( + argv, + capture_output=True, + text=True, + timeout=180, + env={ + **os.environ, + "OPENBOOST_BACKEND": "cpu" if strategy == "legacy_cpu" else "cuda", + "NUMBA_CACHE_DIR": temp, + "CUPY_CACHE_DIR": temp, + }, + ) + cell = ( + json.loads(output.read_text()) + if output.exists() + else dict(strategy=strategy, seed=seed) + ) + cell["worker_stderr"] = run.stderr + if run.returncode: + cell["error"] = dict( + returncode=run.returncode, stdout=run.stdout, stderr=run.stderr + ) + except subprocess.TimeoutExpired: + cell = dict( + strategy=strategy, seed=seed, error="worker timeout after 180 seconds" + ) + cells.append(cell) + # Do not assert quality/speed pass: regressions must survive in the artifact. + if profile_only: + import numpy as np + + parent = json.loads((root / "value_parent.json").read_text())["checks"]["value_cells"] + for cell in cells: + old = next(c for c in parent if c["seed"] == 0 and c["strategy"] == cell["strategy"]) + for key in ("nll", "crps", "coverage90"): + np.testing.assert_allclose( + cell["records"][0]["metrics"][key], + old["records"][1]["metrics"][key], + rtol=1e-4, + atol=1e-5, + ) + checks["profile_quality_matches_parent"] = True + validate_profiles(cells, profile_only=True) + else: + checks["value_summary"] = summarize(cells, frozen) diff --git a/tests/test_batch_histograms.py b/tests/test_batch_histograms.py new file mode 100644 index 0000000..b7c707a --- /dev/null +++ b/tests/test_batch_histograms.py @@ -0,0 +1,78 @@ +"""Independent sample-sum oracle for the experimental histogram contract.""" + +import numpy as np +import pytest + +from openboost.experimental import build_histograms + + +def oracle(bins, grad, hess, ids, active): + shape = (len(active), bins.shape[0], 256) + g, h = np.zeros(shape), np.zeros(shape) + counts = np.zeros(len(active), dtype=np.int32) + for i, node in enumerate(ids): + if node == -1 or not active[node]: + continue + counts[node] += 1 + for f in range(bins.shape[0]): + g[node, f, bins[f, i]] += float(grad[i]) + h[node, f, bins[f, i]] += float(hess[i]) + return g, h, counts + + +def fixture(): + bins = np.array([[0, 1, 255, 1, 2, 0], [3, 3, 3, 3, 3, 3]], dtype=np.uint8) + weights = np.array([1, 0, 2, .5, 3, 1], dtype=np.float32) + grad = np.array([2, 9, -1, 4, 5, 8], dtype=np.float32) * weights + hess = np.array([1, 3, 2, 1, 4, 2], dtype=np.float32) * weights + ids = np.array([0, 0, 2, 2, 1, -1], dtype=np.int32) + active = np.array([True, False, True, True]) + return bins, grad, hess, ids, active + + +def test_sample_sum_oracle(): + args = fixture() + expected = oracle(*args) + batch = build_histograms(*args) + for actual, wanted in zip((batch.grad, batch.hess, batch.counts), expected, strict=True): + np.testing.assert_array_equal(actual, wanted) + assert batch.grad.dtype == batch.hess.dtype == np.float32 + assert batch.counts.tolist() == [2, 0, 2, 0] # zero weight still counts + assert batch.grad[2, 0, 255] == -2 + assert not np.shares_memory(batch.active, args[-1]) + + +def test_empty_samples_and_exact_budget(): + args = (np.zeros((2, 0), np.uint8), np.zeros(0, np.float32), np.zeros(0, np.float32), + np.zeros(0, np.int32), np.ones(3, bool)) + required = 3 * 2 * 256 * 8 + 3 * 5 + result = build_histograms(*args, memory_budget_bytes=required) + assert result.nbytes == required + assert not result.counts.any() and not result.grad.any() + with pytest.raises(MemoryError): + build_histograms(*args, memory_budget_bytes=required - 1) + + +@pytest.mark.parametrize('index,value', [ + (1, np.ones(6, np.float64)), (2, np.full(6, -1, np.float32)), + (1, np.full(6, np.nan, np.float32)), (3, np.full(6, 4, np.int32)), + (3, np.full(6, -2, np.int32)), (4, np.ones(4, np.int32)), + (1, np.ones(12, np.float32)[::2]), +]) +def test_invalid_input(index, value): + args = list(fixture()) + args[index] = value + with pytest.raises((ValueError, TypeError)): + build_histograms(*args) + + +def test_zero_curvature_and_overflow(): + args = list(fixture()) + args[1] = np.zeros(6, np.float32) + args[2] = np.zeros(6, np.float32) + batch = build_histograms(*args) + assert batch.counts.sum() == 4 + assert not batch.grad.any() and not batch.hess.any() + args[1] = np.full(6, np.finfo(np.float32).max, np.float32) + with pytest.raises(ValueError, match='overflow'): + build_histograms(*args) diff --git a/tests/test_batch_leaves.py b/tests/test_batch_leaves.py new file mode 100644 index 0000000..a1c6510 --- /dev/null +++ b/tests/test_batch_leaves.py @@ -0,0 +1,191 @@ +"""Direct row-sum and Newton references for leaf reduction and user rules.""" + +import numpy as np +import pytest + +from openboost.experimental import TrainerConfig, leaf_values, reduce_leaves + + +class BoundedLeaf: + supported_devices = frozenset({"cpu", "cuda"}) + + def values(self, grad, hess, *, config, context): + from openboost.experimental import NewtonLeafRule + + return context.xp.clip( + NewtonLeafRule().values(grad, hess, config=config, context=context), -0.5, 0.5 + ) + + +def example(): + g = np.array([-2, 0, -12, 5, -3, 8], np.float32) + h = np.array([1, 0, 3, 2, 1, 4], np.float32) + ids = np.array([0, 0, 0, 1, 2, -1], np.int32) + active = np.array([True, False, True, True]) + return g, h, ids, active + + +def direct(g, h, ids, active): + G, H, counts = np.zeros(len(active)), np.zeros(len(active)), np.zeros(len(active), np.int32) + for i, node in enumerate(ids): + if node >= 0 and active[node]: + G[node] += float(g[i]) + H[node] += float(h[i]) + counts[node] += 1 + return G, H, counts + + +def test_reduction_and_weighted_newton(): + args = example() + stats = reduce_leaves(*args) + for actual, expected in zip((stats.grad, stats.hess, stats.counts), direct(*args), strict=True): + np.testing.assert_array_equal(actual, expected) + np.testing.assert_allclose(leaf_values(*args), [14 / 5, 0, 3 / 2, 0], rtol=1e-6) + np.testing.assert_array_equal(leaf_values(*args, leaf_rule=BoundedLeaf()), [0.5, 0, 0.5, 0]) + assert stats.counts.tolist() == [3, 0, 1, 0] + + +def test_two_rounds_bounded_values_change_next_gradient(): + y, weights = np.array([2, 4], np.float32), np.array([1, 3], np.float32) + ids, active = np.zeros(2, np.int32), np.array([True]) + results = [] + for rule in (None, BoundedLeaf()): + raw = np.zeros(2, np.float32) + history = [] + for _ in range(2): + g = (raw - y) * weights + history.append(g.copy()) + raw += leaf_values(g, weights.copy(), ids, active, leaf_rule=rule)[ids] + results.append((raw, history)) + np.testing.assert_allclose(results[0][0], [3.36, 3.36], rtol=1e-6) + np.testing.assert_array_equal(results[1][0], [1, 1]) + np.testing.assert_allclose(results[0][1][1], [0.8, -3.6], atol=1e-6) + np.testing.assert_array_equal(results[1][1][1], [-1.5, -10.5]) + + +def test_empty_and_zero_curvature(): + z = np.zeros(0, np.float32) + np.testing.assert_array_equal( + leaf_values( + z, z, np.zeros(0, np.int32), np.ones(3, bool), config=TrainerConfig(reg_lambda=0) + ), + [0, 0, 0], + ) + g, h, ids, active = ( + np.zeros(2, np.float32), + np.zeros(2, np.float32), + np.zeros(2, np.int32), + np.array([True]), + ) + assert leaf_values(g, h, ids, active, config=TrainerConfig(reg_lambda=0))[0] == 0 + g[:] = 1 + with pytest.raises(ValueError, match="curvature"): + leaf_values(g, h, ids, active, config=TrainerConfig(reg_lambda=0)) + + +@pytest.mark.parametrize("kind", ["dtype", "shape", "nan", "inactive", "mutation", "device"]) +def test_bad_rule(kind): + class Bad: + supported_devices = frozenset({"cuda"} if kind == "device" else {"cpu"}) + + def values(self, g, h, *, config, context): + if kind == "mutation": + g[0] = 123 + if kind == "dtype": + return np.zeros(len(g), np.float64) + if kind == "shape": + return np.zeros(len(g) + 1, np.float32) + if kind == "nan": + return np.full(len(g), np.nan, np.float32) + return np.ones(len(g), np.float32) + + with pytest.raises((TypeError, ValueError)): + leaf_values(*example(), leaf_rule=Bad()) + + +@pytest.mark.parametrize( + "index,value", + [ + (0, np.ones(6, np.float64)), + (1, np.full(6, -1, np.float32)), + (2, np.full(6, 4, np.int32)), + (0, np.full(6, np.inf, np.float32)), + ], +) +def test_bad_statistics(index, value): + args = list(example()) + args[index] = value + with pytest.raises((ValueError, TypeError)): + reduce_leaves(*args) + + +def test_default_rule_rejects_unsupported_l1(): + with pytest.raises(ValueError, match="L2"): + leaf_values(*example(), config=TrainerConfig(reg_alpha=1)) + + +def test_bounded_leaf_in_actual_cpu_trainer(): + from openboost.experimental import Booster, BuiltTree + from tests.test_experimental_dispatch import constant + from tests.test_experimental_objective import TwoSquared + + class Observe(TwoSquared): + def __init__(self): + self.history = [] + + def step(self, raw, y, sample_weight=None, extra=None, *, context): + result = super().step(raw, y, sample_weight, extra, context=context) + self.history.append(result["a"][0].copy()) + return result + + class RootBuilder: + supported_devices = frozenset({"cpu"}) + + def __init__(self, rule): + self.rule = rule + + def build(self, binned, grad, hess, *, config, context): + values = leaf_values( + grad, + hess, + np.zeros(len(grad), np.int32), + np.array([True]), + config=config, + context=context, + leaf_rule=self.rule, + ) + return BuiltTree(constant(float(values[0]), binned.n_features)) + + X, y, w = ( + np.zeros((2, 1), np.float32), + np.array([2, 4], np.float32), + np.array([1, 3], np.float32), + ) + models = [] + for rule in (None, BoundedLeaf()): + model = Booster( + objective=Observe(), + tree_builder=RootBuilder(rule), + config=TrainerConfig(n_trees=2, max_depth=0, learning_rate=1), + ).fit(X, y, sample_weight=w) + models.append(model) + np.testing.assert_allclose(models[0].predict_raw(X)["a"], [3.36, 3.36], rtol=1e-6) + np.testing.assert_array_equal(models[1].predict_raw(X)["a"], [1, 1]) + np.testing.assert_allclose(models[0].objective.history[1], [0.8, -3.6], atol=1e-6) + np.testing.assert_array_equal(models[1].objective.history[1], [-1.5, -10.5]) + + +def test_rule_scratch_ownership_and_reduction_overflow(): + class Scratch(BoundedLeaf): + def values(self, *args, **kwargs): + self.buffer = super().values(*args, **kwargs) + return self.buffer + + rule = Scratch() + actual = leaf_values(*example(), leaf_rule=rule) + rule.buffer[:] = 17 + np.testing.assert_array_equal(actual, [0.5, 0, 0.5, 0]) + args = list(example()) + args[0] = np.full(6, np.finfo(np.float32).max, np.float32) + with pytest.raises(ValueError, match="overflow"): + reduce_leaves(*args) diff --git a/tests/test_batch_splits.py b/tests/test_batch_splits.py new file mode 100644 index 0000000..4de4b3a --- /dev/null +++ b/tests/test_batch_splits.py @@ -0,0 +1,155 @@ +"""Exhaustive row-mask split oracle, independent of production histograms.""" + +import numpy as np +import pytest + +from openboost.experimental import build_histograms, find_splits, partition + + +def example(): + bins = np.array([[0, 0, 1, 1, 2, 2, 3, 3], [0, 0, 1, 1, 2, 2, 3, 3]], np.uint8) + weights = np.array([1, 0, 2, 1, 0.5, 1, 3, 1], np.float32) + g = np.array([4, 9, 2, 2, -1, -1, -5, -5], np.float32) * weights + return bins, g, weights, np.zeros(8, np.int32), np.array([True] + [False] * 6) + + +def exhaustive(bins, g, h, ids, active, reg_lambda=1.0, min_child_weight=1.0, min_gain=0.0): + answer = [] + for node in range(len(active)): + best = (-1, -1, 0.0) + rows = ids == node + if active[node] and 2 * node + 2 < len(active): + G, H = sum(float(x) for x in g[rows]), sum(float(x) for x in h[rows]) + if H > 0: + for f in range(len(bins)): + for threshold in range(255): + left = rows & (bins[f] <= threshold) + right = rows & ~left + GL, HL = sum(float(x) for x in g[left]), sum(float(x) for x in h[left]) + GR, HR = sum(float(x) for x in g[right]), sum(float(x) for x in h[right]) + if HL <= 0 or HR <= 0 or min(HL, HR) < min_child_weight: + continue + gain = ( + GL**2 / (HL + reg_lambda) + + GR**2 / (HR + reg_lambda) + - G**2 / (H + reg_lambda) + ) + if gain > best[2] and gain >= min_gain: + best = (f, threshold, gain) + answer.append(best) + return answer + + +@pytest.mark.parametrize( + "kwargs", + [ + {}, + {"reg_lambda": 0.0, "min_child_weight": 0.0}, + {"min_child_weight": 20.0}, + {"min_gain": 1e6}, + ], +) +def test_exhaustive_split_oracle(kwargs): + args = example() + splits = find_splits(build_histograms(*args), **kwargs) + expected = exhaustive(*args, **kwargs) + np.testing.assert_array_equal(splits.feature, [x[0] for x in expected]) + np.testing.assert_array_equal(splits.threshold, [x[1] for x in expected]) + np.testing.assert_allclose(splits.gain, [x[2] for x in expected], rtol=1e-12, atol=1e-12) + np.testing.assert_array_equal(splits.valid, [x[0] >= 0 for x in expected]) + assert splits.feature[0] in (-1, 0) # duplicate feature: first feature wins + + +def test_exact_gain_boundary_and_threshold_tie(): + bins = np.array([[0, 0, 2, 2]], np.uint8) + g = np.array([2, 2, -2, -2], np.float32) + hist = build_histograms( + bins, g, np.ones(4, np.float32), np.zeros(4, np.int32), np.array([True, False, False]) + ) + result = find_splits(hist, reg_lambda=0.0, min_gain=16.0, min_child_weight=2.0) + assert result.gain[0] == 16.0 and result.threshold[0] == 0 and result.valid[0] + assert not find_splits(hist, reg_lambda=0.0, min_gain=np.nextafter(16.0, np.inf)).valid.any() + + +def test_routed_rows_make_real_child_histograms(): + bins, g, h, ids, active = example() + ids[-1] = -1 + hist = build_histograms(bins, g, h, ids, active) + splits = find_splits(hist) + original = ids.copy() + routed = partition(bins, ids, splits) + expected = ids.copy() + for i, node in enumerate(ids): + if node >= 0 and splits.valid[node]: + expected[i] = 2 * node + ( + 1 if bins[splits.feature[node], i] <= splits.threshold[node] else 2 + ) + np.testing.assert_array_equal(routed, expected) + np.testing.assert_array_equal(ids, original) + assert not np.shares_memory(routed, ids) + next_active = np.array([False, True, True, False, False, False, False]) + children = build_histograms(bins, g, h, routed, next_active) + for node in (1, 2): + assert children.counts[node] == (expected == node).sum() + assert children.grad[node, 0].sum() == g[expected == node].sum() + assert children.hess[node, 0].sum() == h[expected == node].sum() + second = find_splits(children) + expected_second = exhaustive(bins, g, h, expected, next_active) + np.testing.assert_array_equal(second.feature, [x[0] for x in expected_second]) + np.testing.assert_array_equal(second.threshold, [x[1] for x in expected_second]) + + +@pytest.mark.parametrize("kind", ["constant", "zero", "terminal", "inactive"]) +def test_no_legal_split(kind): + bins, g, h, ids, active = example() + if kind == "constant": + bins[:] = 2 + if kind == "zero": + h[:] = 0 + if kind == "terminal": + ids[:] = 6 + active[:] = False + active[6] = True + if kind == "inactive": + active[:] = False + result = find_splits(build_histograms(bins, g, h, ids, active), min_child_weight=0.0) + assert not result.valid.any() + assert (result.feature == -1).all() and (result.left_child == -1).all() + np.testing.assert_array_equal(partition(bins, ids, result), ids) + + +def test_missing_and_invalid_routing_rejected(): + args = list(example()) + hist = build_histograms(*args) + splits = find_splits(hist) + bad_ids = np.full(8, 99, np.int32) + with pytest.raises(ValueError): + partition(args[0], bad_ids, splits) + splits.left_child[0] = 4 + with pytest.raises(ValueError): + partition(args[0], args[3], splits) + args[0][0, 0] = 255 + with pytest.raises(ValueError, match="missing"): + find_splits(build_histograms(*args)) + with pytest.raises(ValueError, match="missing"): + partition(args[0], args[3], find_splits(hist)) + + +@pytest.mark.parametrize( + "kwargs", [{"reg_lambda": -1}, {"min_gain": np.inf}, {"min_child_weight": np.nan}] +) +def test_bad_parameters(kwargs): + with pytest.raises(ValueError): + find_splits(build_histograms(*example()), **kwargs) + + +def test_empty_partition_and_negative_gain(): + args = example() + hist = build_histograms(args[0][:, :0].copy(), args[1][:0], args[2][:0], args[3][:0], args[4]) + empty = find_splits(hist) + assert partition(args[0][:, :0].copy(), args[3][:0], empty).shape == (0,) + # Equal gradients in every row with L2: splitting has negative gain. + hist = build_histograms( + args[0], np.ones(8, np.float32), np.ones(8, np.float32), args[3], args[4] + ) + assert not find_splits(hist).valid.any() diff --git a/tests/test_categorical.py b/tests/test_categorical.py index 868a4f9..71eaab2 100644 --- a/tests/test_categorical.py +++ b/tests/test_categorical.py @@ -178,6 +178,16 @@ def test_categorical_split_found(self): class TestGradientBoostingWithCategorical: """Tests for GradientBoosting with categorical features.""" + + def test_fit_rejects_unrepresentable_categorical_split(self): + """Tree training fails before silently truncating a category bitset.""" + categories = np.tile(np.arange(65, dtype=np.float32), 4) + X_binned = array(categories[:, None], categorical_features=[0]) + y = (categories % 2).astype(np.float32) + + model = GradientBoosting(n_trees=1, max_depth=1) + with pytest.raises(ValueError, match="maximum supported is 64"): + model.fit(X_binned, y) def test_fit_with_categorical(self): """GradientBoosting fits with categorical features.""" diff --git a/tests/test_experimental_dispatch.py b/tests/test_experimental_dispatch.py new file mode 100644 index 0000000..4cff88a --- /dev/null +++ b/tests/test_experimental_dispatch.py @@ -0,0 +1,135 @@ +"""Builder priority and exactly-once channel updates, with independent oracles.""" + +import numpy as np +import pytest + +from openboost.experimental import ( + Booster, + BuiltTree, + CPUHistogramBuilder, + TrainerConfig, + TreeStructure, +) +from tests.test_experimental_objective import TwoSquared + + +def constant(value, n_features=1): + return TreeStructure(features=np.array([-1], dtype=np.int32), thresholds=np.array([-1], dtype=np.int32), + left_children=np.array([-1], dtype=np.int32), right_children=np.array([-1], dtype=np.int32), + values=np.array([value], dtype=np.float32), n_nodes=1, depth=0, n_features=n_features) + + +class PresetBuilder: + supported_devices = frozenset({'cpu'}) + + def __init__(self): + self.calls = [] + + def build(self, binned, grad, hess, *, config, context): + assert not grad.flags.writeable and not hess.flags.writeable + assert not binned.data.flags.writeable + self.calls.append((context.round_idx, context.channel)) + value = 2. if context.channel == 'a' else 4. + return BuiltTree(constant(value), np.full(len(grad), value, dtype=np.float32)) + + +class Decay: + def coefficients(self, round_idx, channel_names, base_learning_rate): + return {'a': base_learning_rate / (round_idx+1), 'b': base_learning_rate / (2*(round_idx+1))} + + +def test_builder_priority_and_exact_updates(monkeypatch): + import openboost._trainer as trainer + from openboost._callbacks import Callback + # Even an eligible input must never bypass the explicitly selected builder. + monkeypatch.setattr(trainer, '_gpu_native_eligible', lambda *args: True) + def wrong(*args, **kwargs): + raise AssertionError('Native dispatch bypassed explicit builder') + monkeypatch.setattr(trainer, 'fit_tree_gpu_native', wrong) + class Recording(TwoSquared): + def loss_value(self, raw, *args, **kwargs): + self.final_raw = {k: v.copy() for k, v in raw.items()} + return super().loss_value(raw, *args, **kwargs) + objective, builder = Recording(), PresetBuilder() + X = np.zeros((4, 1), dtype=np.float32) + model = Booster(objective=objective, tree_builder=builder, step_schedule=Decay(), config=TrainerConfig(n_trees=2, learning_rate=.5)).fit(X, np.arange(4, dtype=np.float32), callbacks=[Callback()]) + assert builder.calls == [(0,'a'), (0,'b'), (1,'a'), (1,'b')] + assert model.coefficients_ == {'a': [.5,.25], 'b': [.25,.125]} + for k, expected in [('a', 1.5), ('b', 2.5)]: + np.testing.assert_array_equal(model.predict_raw(X)[k], np.full(4, expected, dtype=np.float32)) + np.testing.assert_array_equal(model.predict_raw(X)[k], objective.final_raw[k]) + + +@pytest.mark.parametrize('values', [{'a': .1}, {'a': -.1, 'b': .1}, {'a': np.nan, 'b': .1}]) +def test_schedule_rejected_before_build(values): + class Bad: + def coefficients(self, *args): + return values + builder = PresetBuilder() + with pytest.raises(ValueError): + Booster(objective=TwoSquared(), tree_builder=builder, step_schedule=Bad(), config=TrainerConfig(n_trees=1)).fit(np.zeros((4,1)), np.ones(4)) + assert builder.calls == [] + + +def test_cached_prediction_must_match_tree(): + class Bad(PresetBuilder): + def build(self, binned, grad, hess, **kwargs): + return BuiltTree(constant(1), np.zeros(len(grad), dtype=np.float32)) + with pytest.raises(ValueError, match='prediction'): + Booster(objective=TwoSquared(), tree_builder=Bad(), config=TrainerConfig(n_trees=1)).fit(np.zeros((4,1)), np.ones(4)) + + +def test_cpu_split_gain_scale_and_newton(): + X = np.array([[0], [0], [1], [1]], dtype=np.float32) + y = np.array([-2,-2,2,2], dtype=np.float32) + # At channel a: G_left=4, G_right=-4, H_child=2, lambda=1. + # Unhalved gain is 16/3 + 16/3 = 32/3. min_gain=10 splits; 11 rejects. + for gain, leaves in [(10., [-4/3,-4/3,4/3,4/3]), (11., [0,0,0,0])]: + model = Booster(objective=TwoSquared(), config=TrainerConfig(n_trees=1, max_depth=1, min_gain=gain, learning_rate=1)).fit(X,y) + np.testing.assert_allclose(model.predict_raw(X)['a'], leaves, rtol=1e-6) + + +def test_zero_curvature_root(): + class Zero(TwoSquared): + def step(self, raw, y, *args, **kwargs): + return {k: (np.zeros_like(y), np.zeros_like(y)) for k in self.channel_names} + X, y = np.zeros((4,1)), np.ones(4) + model = Booster(objective=Zero(), config=TrainerConfig(n_trees=1, reg_lambda=0)).fit(X,y) + np.testing.assert_array_equal(model.predict_raw(X)['a'], np.zeros(4)) + class Invalid(Zero): + def step(self, raw, y, *args, **kwargs): + return {k: (np.ones_like(y), np.zeros_like(y)) for k in self.channel_names} + with pytest.raises(ValueError, match='curvature'): + Booster(objective=Invalid(), config=TrainerConfig(n_trees=1, reg_lambda=0)).fit(X,y) + + +def test_default_builder_is_cpu_only(): + assert CPUHistogramBuilder.supported_devices == frozenset({'cpu'}) + + +def test_builder_scratch_tree_is_detached(): + class Reuse(PresetBuilder): + def __init__(self): + self.tree = constant(0) + def build(self, binned, grad, hess, *, config, context): + self.tree.values[0] = 2 if context.channel == 'a' else 4 + return BuiltTree(self.tree) + model = Booster(objective=TwoSquared(), tree_builder=Reuse(), config=TrainerConfig(n_trees=1, learning_rate=1)).fit(np.zeros((4,1)), np.ones(4)) + assert model.trees_['a'][0].values[0] == 2 + assert model.trees_['b'][0].values[0] == 4 + + +def test_invalid_tree_routing_rejected(): + class Bad(PresetBuilder): + def build(self, binned, grad, hess, **kwargs): + tree = constant(1) + tree.left_children[0] = tree.right_children[0] = 0 + tree.features[0] = tree.thresholds[0] = 0 + return BuiltTree(tree) + with pytest.raises(ValueError, match='cyclic'): + Booster(objective=TwoSquared(), tree_builder=Bad(), config=TrainerConfig(n_trees=1)).fit(np.zeros((4,1)), np.ones(4)) + + +def test_unsupported_zero_regularization_pair(): + with pytest.raises(ValueError, match='positive min_child_weight'): + Booster(objective=TwoSquared(), config=TrainerConfig(reg_lambda=0, min_child_weight=0)).fit(np.zeros((4,1)), np.ones(4)) diff --git a/tests/test_experimental_gpu_preflight.py b/tests/test_experimental_gpu_preflight.py new file mode 100644 index 0000000..c6d9dbe --- /dev/null +++ b/tests/test_experimental_gpu_preflight.py @@ -0,0 +1,53 @@ +"""Strict GPU capability errors must precede device imports and objective work.""" + +import numpy as np +import pytest + +from openboost.experimental import Booster, LevelWiseBuilder, TrainerConfig +from tests.test_experimental_objective import TwoSquared + + +class DeclaredGPU(TwoSquared): + supported_devices = frozenset({"cpu", "cuda"}) + + +@pytest.mark.parametrize( + "kind", ["missing", "sampling", "l1", "eval", "callback", "early_stop", "budget", "categorical"] +) +def test_gpu_preflight_without_cuda(kind): + X, y = np.zeros((4, 1), np.float32), np.ones(4, np.float32) + config, kwargs = TrainerConfig(n_trees=1), {} + if kind == "missing": + X[0, 0] = np.nan + if kind == "categorical": + import openboost as ob + + X = ob.array(X, categorical_features=[0]) + if kind == "sampling": + config.subsample = 0.5 + if kind == "l1": + config.reg_alpha = 1 + if kind == "eval": + kwargs["eval_sets"] = [{"X": X, "y": y}] + if kind == "callback": + kwargs["callbacks"] = [object()] + if kind == "early_stop": + kwargs["early_stopping_rounds"] = 1 + with pytest.raises(ValueError, match="Strict CUDA"): + Booster( + objective=DeclaredGPU(), + tree_builder=LevelWiseBuilder( + memory_budget_bytes=0 if kind == "budget" else 256 * 1024**2 + ), + config=config, + device="cuda", + ).fit(X, y, **kwargs) + + +def test_unsupported_objective_full_cpu_fallback(): + with pytest.warns(RuntimeWarning, match="CPU fallback"): + model = Booster( + objective=TwoSquared(), device="cuda", fallback="warn", config=TrainerConfig(n_trees=1) + ).fit(np.zeros((4, 1)), np.ones(4)) + assert model.fit_report_["actual_device"] == "cpu" + assert model.fit_report_["fallback_reason"] diff --git a/tests/test_experimental_objective.py b/tests/test_experimental_objective.py new file mode 100644 index 0000000..55b1bb1 --- /dev/null +++ b/tests/test_experimental_objective.py @@ -0,0 +1,158 @@ +"""Independent CPU objective and contract failures for the experimental facade.""" + +import numpy as np +import pytest + +from openboost.experimental import Booster, DistributionObjectiveAdapter, TrainerConfig + + +class TwoSquared: + channel_names = ('a', 'b') + supported_devices = frozenset({'cpu'}) + + def init_raw(self, y, sample_weight=None, extra=None): + return {'a': 0., 'b': 1.} + + def step(self, raw, y, sample_weight=None, extra=None, *, context): + assert context.device == 'cpu' and context.channel is None + assert not raw['a'].flags.writeable + w = np.ones_like(y) if sample_weight is None else sample_weight + return {k: ((raw[k] - y) * w, w.copy()) for k in self.channel_names} + + def loss_value(self, raw, y, sample_weight=None, extra=None, *, context): + return np.average(sum((raw[k] - y) ** 2 for k in self.channel_names) / 2, weights=sample_weight) + + def constrain(self, raw, extra=None): + return raw + + +@pytest.fixture +def data(): + return np.zeros((4, 1), dtype=np.float32), np.array([1, 2, 3, 4], dtype=np.float32) + + +def test_two_channel_weighted_newton(data): + X, y = data + weights = np.array([0, 1, 2, 4], dtype=np.float32) + model = Booster(objective=TwoSquared(), config=TrainerConfig(n_trees=1, max_depth=0, learning_rate=.5)).fit(X, y, sample_weight=weights) + # G_a=-24, G_b=-17, H=7, lambda=1. Weight is applied exactly once. + result = model.predict_raw(X) + np.testing.assert_array_equal(result['a'], np.full(4, 1.5, dtype=np.float32)) + np.testing.assert_array_equal(result['b'], np.full(4, 2.0625, dtype=np.float32)) + assert model.fit_report_['actual_device'] == 'cpu' + + +@pytest.mark.parametrize('fault', ['keys', 'shape', 'dtype', 'nan', 'negative_hess', 'alias', 'raw_alias']) +def test_bad_objective_output(data, fault): + class Bad(TwoSquared): + def step(self, raw, y, *args, **kwargs): + output = super().step(raw, y, *args, **kwargs) + g, h = output['a'] + if fault == 'keys': + output.pop('b') + elif fault == 'shape': + output['a'] = (g[:, None], h) + elif fault == 'dtype': + output['a'] = (g.astype('float64'), h) + elif fault == 'nan': + g[0] = np.nan + elif fault == 'negative_hess': + h[0] = -1 + elif fault == 'alias': + output['b'] = output['a'] + else: + output['a'] = (raw['a'], h) + return output + model = Booster(objective=Bad(), config=TrainerConfig(n_trees=1)) + with pytest.raises((ValueError, TypeError)): + model.fit(*data) + with pytest.raises(RuntimeError, match='not fitted'): + model.predict_raw(data[0]) + + +@pytest.mark.parametrize('weight', [[0]*4, [-1,1,1,1], [np.nan,1,1,1], [np.inf,1,1,1], [1,1]]) +def test_invalid_weights(data, weight): + with pytest.raises(ValueError): + Booster(objective=TwoSquared()).fit(*data, sample_weight=weight) + + +def test_target_shape_and_capabilities(data): + with pytest.raises(ValueError, match='1D'): + Booster(objective=TwoSquared()).fit(data[0], data[1][:, None]) + class Missing(TwoSquared): + supported_devices = None + with pytest.raises(ValueError, match='supported_devices'): + Booster(objective=Missing()).fit(*data) + + +def test_builtin_adapter_parity(data): + import openboost as ob + from openboost._trainer import predict_raw + X, y = data + config = TrainerConfig(n_trees=2, max_depth=1) + actual = Booster(objective=DistributionObjectiveAdapter('normal', natural=True), config=config).fit(X, y) + reference = ob.NaturalBoostNormal(n_trees=2, max_depth=1).fit(X, y) + for k, v in predict_raw(reference, X).items(): + np.testing.assert_array_equal(actual.predict_raw(X)[k], v) + + +def test_cuda_preflight_and_report(data): + with pytest.raises(ValueError, match='CPU'): + Booster(objective=TwoSquared(), device='cuda').fit(*data) + with pytest.warns(RuntimeWarning, match='CPU'): + model = Booster(objective=TwoSquared(), device='cuda', fallback='warn', config=TrainerConfig(n_trees=1)).fit(*data) + assert model.fit_report_['requested_device'] == 'cuda' + assert model.fit_report_['actual_device'] == 'cpu' + + +@pytest.mark.parametrize('kwargs', [{'n_trees': 0}, {'max_depth': 9}, {'n_bins': 255}, {'reg_lambda': -1}, {'min_gain': float('inf')}, {'subsample': 0}]) +def test_unsupported_config_preflight(data, kwargs): + model = Booster(objective=TwoSquared(), config=TrainerConfig(**kwargs)) + with pytest.raises(ValueError): + model.fit(*data) + assert not model.trees_ + + +def test_plugin_inputs_and_context_are_readonly(data): + class Inspect(TwoSquared): + def step(self, raw, y, sample_weight=None, extra=None, *, context): + with pytest.raises(ValueError): + raw['a'][0] = 10 + with pytest.raises(AttributeError): + context.round_idx = 20 + with pytest.raises(TypeError): + raw['other'] = y + return super().step(raw, y, sample_weight, extra, context=context) + Booster(objective=Inspect(), config=TrainerConfig(n_trees=1)).fit(*data) + + +def test_seed_shared_with_builder_and_global_rng_unchanged(): + class RandomObjective(TwoSquared): + def step(self, raw, y, sample_weight, extra, *, context): + self.draw = context.rng.random() + return super().step(raw, y, sample_weight, extra, context=context) + + from openboost.experimental import CPUHistogramBuilder + + class WatchBuilder(CPUHistogramBuilder): + def build(self, binned, grad, hess, *, config, context): + self.draw = context.rng.random() + return super().build(binned, grad, hess, config=config, context=context) + + rng = np.random.default_rng(42) + X = rng.normal(size=(32, 3)).astype(np.float32) + y = X[:, 0].copy() + before = np.random.get_state() + models = [] + for _ in range(2): + objective, builder = RandomObjective(), WatchBuilder() + models.append(Booster(objective=objective, tree_builder=builder, + config=TrainerConfig(n_trees=2, max_depth=2, random_state=13, + subsample=.75, colsample_bytree=.67)).fit(X, y)) + after = np.random.get_state() + assert before[0] == after[0] and before[2:] == after[2:] + np.testing.assert_array_equal(before[1], after[1]) + assert models[0].objective.draw == models[1].objective.draw + assert models[0].tree_builder.draw == models[1].tree_builder.draw + for k, v in models[0].predict_raw(X).items(): + np.testing.assert_array_equal(v, models[1].predict_raw(X)[k]) diff --git a/tests/test_experimental_persistence.py b/tests/test_experimental_persistence.py new file mode 100644 index 0000000..4f14e05 --- /dev/null +++ b/tests/test_experimental_persistence.py @@ -0,0 +1,176 @@ +"""Coefficient persistence and inference without training plugin objects.""" + +import joblib +import numpy as np +import pytest + +import openboost as ob +from openboost.experimental import Booster, TrainerConfig +from tests.test_experimental_dispatch import Decay, PresetBuilder +from tests.test_experimental_objective import TwoSquared + + +class NoPickleObjective(TwoSquared): + def __reduce__(self): + raise AssertionError("Do not serialize the objective") + + +class NoPickleBuilder(PresetBuilder): + def __reduce__(self): + raise AssertionError("Do not serialize the builder") + + +class NoPickleSchedule(Decay): + def __reduce__(self): + raise AssertionError("Do not serialize the schedule") + + +def test_plugins_excluded_and_nonconstant_coefficients_roundtrip(tmp_path): + X, y = np.zeros((4, 1)), np.ones(4) + model = Booster( + objective=NoPickleObjective(), + tree_builder=NoPickleBuilder(), + step_schedule=NoPickleSchedule(), + config=TrainerConfig(n_trees=2, learning_rate=0.5), + ).fit(X, y) + path = tmp_path / "model.ob" + model.save(path) + state = joblib.load(path) + assert not {"objective", "tree_builder", "step_schedule", "config"} & set(state) + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(path) + assert loaded.coefficients_ == model.coefficients_ + for k, v in model.predict_raw(X).items(): + np.testing.assert_array_equal(loaded.predict_raw(X)[k], v) + with pytest.raises(RuntimeError, match="inference"): + loaded.fit(X, y) + + +def test_early_stop_restores_coefficients_and_checkpoint(tmp_path): + from openboost._callbacks import ModelCheckpoint + + class Curve(TwoSquared): + calls = 0 + + def loss_value(self, raw, y, *args, **kwargs): + if np.all(y < 0): + result = self.calls + self.calls += 1 + return float(result) + return super().loss_value(raw, y, *args, **kwargs) + + X, y = np.zeros((4, 1)), np.ones(4) + path = tmp_path / "best.ob" + model = Booster( + objective=Curve(), + tree_builder=PresetBuilder(), + step_schedule=Decay(), + config=TrainerConfig(n_trees=5, learning_rate=0.5), + ) + model.fit( + X, + y, + eval_sets=[{"X": X, "y": -y}], + callbacks=[ModelCheckpoint(str(path))], + early_stopping_rounds=1, + ) + assert model.best_iteration_ == 0 + assert all(len(t) == 1 for t in model.trees_.values()) + assert model.coefficients_ == {"a": [0.5], "b": [0.25]} + assert model.fit_report_["tree_counts"] == {"a": 1, "b": 1} + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(path) + for k, v in model.predict_raw(X).items(): + np.testing.assert_array_equal(loaded.predict_raw(X)[k], v) + + +@pytest.mark.parametrize("kind", ["numeric", "missing", "categorical"]) +def test_default_tree_and_binner_state_roundtrip(tmp_path, kind): + rng = np.random.default_rng(19) + X = rng.normal(size=(64, 2)).astype(np.float32) + y = (X[:, 0] > 0).astype(np.float32) + if kind == "missing": + X[::4, 0] = np.nan + raw_X = X.copy() + if kind == "categorical": + X[:, 0] = np.arange(len(X)) % 3 + raw_X = X.copy() + X = ob.array(X, categorical_features=[0]) + model = Booster( + objective=TwoSquared(), step_schedule=Decay(), config=TrainerConfig(n_trees=3, max_depth=2) + ).fit(X, y) + path = tmp_path / "state.ob" + model.save(path) + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(path) + for k, v in model.predict_raw(raw_X).items(): + np.testing.assert_array_equal(loaded.predict_raw(raw_X)[k], v) + + +def test_legacy_missing_coefficients_and_invalid_counts(tmp_path): + X, y = np.zeros((4, 1)), np.ones(4) + model = Booster(objective=TwoSquared(), config=TrainerConfig(n_trees=2)).fit(X, y) + state = model._to_state_dict() + state.pop("coefficients_") + path = tmp_path / "legacy.ob" + joblib.dump(state, path) + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(path) + assert loaded.coefficients_ == {"a": [0.1, 0.1], "b": [0.1, 0.1]} + for k, v in model.predict_raw(X).items(): + np.testing.assert_array_equal(loaded.predict_raw(X)[k], v) + state["coefficients_"] = {"a": [0.1], "b": [0.1]} + joblib.dump(state, path) + with pytest.warns(UserWarning, match="trusted"), pytest.raises(ValueError, match="Coefficient"): + Booster.load(path) + + +def test_callback_round_begin_and_lr_mutation(): + from openboost._callbacks import Callback + + class Watch(Callback): + def __init__(self): + self.rounds = [] + + def on_round_begin(self, state): + self.rounds.append(state.round_idx) + + cb = Watch() + Booster(objective=TwoSquared(), config=TrainerConfig(n_trees=2)).fit( + np.zeros((4, 1)), np.ones(4), callbacks=[cb] + ) + assert cb.rounds == [0, 1] + + +@pytest.mark.parametrize("hook", ["on_round_end", "on_train_end"]) +def test_callback_cannot_silently_change_learning_rate(hook): + from openboost._callbacks import Callback + + def mutate(self, state): + state.model.learning_rate = 12 + return True + + cb = type("Mutate", (Callback,), {hook: mutate})() + model = Booster(objective=TwoSquared(), config=TrainerConfig(n_trees=1)) + with pytest.raises(ValueError, match="StepSchedule"): + model.fit(np.zeros((4, 1)), np.ones(4), callbacks=[cb]) + assert model.trees_ == {} + + +@pytest.mark.parametrize( + "changes", + [ + {"_experimental_version": 2}, + {"_serialization_version": 99}, + {"_serialization_version": 1, "_is_categorical": np.array([True])}, + {"coefficients_": {"a": [float("nan")], "b": [0.1]}}, + ], +) +def test_invalid_persistence_state_rejected(changes): + model = Booster(objective=TwoSquared(), config=TrainerConfig(n_trees=1)).fit( + np.zeros((4, 1)), np.ones(4) + ) + state = model._to_state_dict() + state.update(changes) + with pytest.raises(ValueError): + Booster.__new__(Booster)._from_state_dict(state) diff --git a/tests/test_foundation_baseline.py b/tests/test_foundation_baseline.py new file mode 100644 index 0000000..283ee8e --- /dev/null +++ b/tests/test_foundation_baseline.py @@ -0,0 +1,89 @@ +"""Offline baseline gates and independent data/metric checks.""" + +import copy + +import numpy as np +import pytest +from benchmarks.foundation.baseline_worker import normal_metrics +from benchmarks.foundation.dataset import load_housing, split_indices +from benchmarks.foundation.runner import validate_baseline + + +def valid_cells(): + record = { + "fit_s": 1.0, + "predict_params_s": 0.1, + "fallback_warnings": [], + "metrics": {"nll": 1.0, "crps": 0.5, "coverage90": 0.9}, + } + cells = [] + for seed in (0, 1, 2): + for mode in ("resident", "eval"): + for backend in ("cpu", "cuda"): + records = [] + for phase in ("first_fit", "repeat_fit"): + r = copy.deepcopy(record) + r.update( + phase=phase, + fit_path={ + "objective_calls": 30, + "native_tree_calls": 60 if backend == "cuda" else 0, + }, + ) + records.append(r) + cells.append(dict(seed=seed, mode=mode, backend=backend, records=records)) + return cells + + +def test_complete_baseline(): + validate_baseline(valid_cells()) + + +@pytest.mark.parametrize( + "fault", ["missing", "duplicate", "phase", "fallback", "device", "time", "metric", "quality"] +) +def test_bad_baseline_rejected(fault): + cells = valid_cells() + r = cells[1]["records"][1] + if fault == "missing": + cells.pop() + elif fault == "duplicate": + cells[1] = cells[0] + elif fault == "phase": + r["phase"] = "first_fit" + elif fault == "fallback": + r["fallback_warnings"] = ["host"] + elif fault == "device": + r["fit_path"]["native_tree_calls"] = 0 + elif fault == "time": + r["fit_s"] = float("nan") + elif fault == "metric": + r["metrics"]["crps"] = float("inf") + else: + r["metrics"]["nll"] = 2.0 + with pytest.raises(ValueError): + validate_baseline(cells) + + +def test_normal_metrics_at_mean(): + result = normal_metrics(np.zeros(3), {"loc": np.zeros(3), "scale": np.ones(3)}) + assert result["nll"] == pytest.approx(0.5 * np.log(2 * np.pi)) + assert result["crps"] == pytest.approx((np.sqrt(2) - 1) / np.sqrt(np.pi)) + assert result["coverage90"] == 1.0 + + +def test_splits_are_disjoint_complete_seeded(): + a = split_indices(20640, 0) + b = split_indices(20640, 0) + assert [len(x) for x in a] == [12384, 4128, 4128] + np.testing.assert_array_equal(np.sort(np.concatenate(a)), np.arange(20640)) + for x, y in zip(a, b, strict=True): + np.testing.assert_array_equal(x, y) + assert not np.array_equal(a[0], split_indices(20640, 1)[0]) + + +def test_corrupt_archive_rejected(tmp_path): + path = tmp_path / "bad.tgz" + path.write_bytes(b"bad") + with pytest.raises(ValueError, match="hash mismatch"): + load_housing(path) diff --git a/tests/test_foundation_contracts.py b/tests/test_foundation_contracts.py new file mode 100644 index 0000000..5bedc3c --- /dev/null +++ b/tests/test_foundation_contracts.py @@ -0,0 +1,152 @@ +"""Host regressions for the unified trainer's execution policy and RNG.""" + +import sys +from types import SimpleNamespace + +import numpy as np +import pytest + +import openboost as ob +import openboost._trainer as trainer +from openboost._distributions import Normal, Poisson +from openboost._objectives import DistributionObjective + + +def test_same_name_custom_distribution_is_not_device_capable(): + custom = type("Normal", (Normal,), {})() + assert not DistributionObjective(custom).device_capable + assert DistributionObjective(Normal()).device_capable + assert DistributionObjective(Poisson()).device_capable + + +def test_kernel_error_propagates(monkeypatch): + def fail(*args, **kwargs): + raise RuntimeError("kernel sentinel") + + monkeypatch.setitem( + sys.modules, + "openboost._backends._cuda", + SimpleNamespace(normal_step_gpu=fail, poisson_step_gpu=fail, scale_gh_gpu=fail), + ) + objective = DistributionObjective(Normal(), natural=True) + with pytest.raises(RuntimeError, match="kernel sentinel"): + objective._step_device({"loc": np.zeros(4), "scale": np.zeros(4)}, np.ones(4), None) + + +@pytest.mark.parametrize("parameter", ["subsample", "colsample_bytree"]) +def test_gpu_sampling_rejected_before_binning(parameter, monkeypatch): + monkeypatch.setattr(trainer, "is_cuda", lambda: True) + model = ob.NaturalBoost(**{parameter: 0.5}) + with pytest.raises(ValueError, match="sampling"): + model.fit(np.ones((8, 2)), np.ones(8)) + assert model.X_binned_ is None + assert not model.trees_ + with pytest.raises(RuntimeError, match="not fitted"): + model.predict(np.ones((8, 2))) + + +def test_failed_first_step_leaves_unfitted_model(monkeypatch): + def fail(*args, **kwargs): + raise RuntimeError("objective sentinel") + + monkeypatch.setattr(DistributionObjective, "step", fail) + model = ob.NaturalBoost(n_trees=1) + with pytest.raises(RuntimeError, match="objective sentinel"): + model.fit(np.ones((8, 2)), np.ones(8)) + assert not model.trees_ + assert model.X_binned_ is None + + +@pytest.mark.parametrize("parameter", ["subsample", "colsample_bytree"]) +def test_sampling_uses_scoped_model_seed(parameter): + rng = np.random.default_rng(78) + X = rng.normal(size=(128, 6)).astype(np.float32) + y = (X[:, 0] + X[:, 1] ** 2 + rng.normal(size=128)).astype(np.float32) + state = np.random.get_state() + predictions = [] + for seed in (12, 12, 34): + model = ob.NaturalBoost(n_trees=3, max_depth=2, random_state=seed, **{parameter: 0.5}) + model.fit(X, y) + predictions.append(model.predict(X)) + after = np.random.get_state() + assert state[0] == after[0] and state[2:] == after[2:] + np.testing.assert_array_equal(state[1], after[1]) + np.testing.assert_array_equal(predictions[0], predictions[1]) + assert not np.array_equal(predictions[0], predictions[2]) + + +def test_known_host_objective_fallback_is_visible(monkeypatch): + class StopBeforeDevice(Exception): + pass + + def stop(*args, **kwargs): + raise StopBeforeDevice + + monkeypatch.setattr(trainer, "is_cuda", lambda: True) + monkeypatch.setattr(trainer, "_bin_features", stop) + custom = type("Normal", (Normal,), {})() + with ( + pytest.warns(RuntimeWarning, match="objective fallback to CPU"), + pytest.raises(StopBeforeDevice), + ): + ob.NaturalBoost(distribution=custom).fit(np.ones((8, 2)), np.ones(8)) + + +@pytest.mark.parametrize( + "weights,expected", [(None, 1.0), ([1.0] * 8, 0.0), ([0, 1, 2, 4, 0, 2, 3, 5], 0.0)] +) +def test_weighted_native_hint_selection(weights, expected, monkeypatch): + """Host policy check only; real histogram/Newton parity is the Modal gate.""" + import numba + + from openboost._array import BinnedArray + + class StopAtNative(Exception): + pass + + class FixedObjective: + channel_names = ["value"] + device_capable = False + unit_hessian = True + + def init_raw(self, *args): + return {"value": 0.0} + + def step(self, raw, y, sample_weight, extra): + h = np.ones(8, dtype=np.float32) if sample_weight is None else sample_weight + return {"value": (y * h, h)} + + captured = {} + + def native(*args, **kwargs): + captured.update(kwargs) + raise StopAtNative + + monkeypatch.setattr(trainer, "is_cuda", lambda: True) + monkeypatch.setattr(numba, "cuda", SimpleNamespace(to_device=lambda a: a)) + monkeypatch.setattr(trainer, "fit_tree_gpu_native", native) + binned = BinnedArray(np.zeros((1, 8), dtype=np.uint8), [], 1, 8, "cpu") + with pytest.warns(RuntimeWarning, match="fallback"), pytest.raises(StopAtNative): + trainer.fit_boosting( + SimpleNamespace(), + FixedObjective(), + binned, + np.ones(8), + config=trainer.TrainerConfig(n_trees=1), + sample_weight=weights, + ) + assert captured["const_hess"] == expected + + +def test_seed_survives_persistence(tmp_path): + rng = np.random.default_rng(9) + X = rng.normal(size=(64, 3)).astype(np.float32) + y = (X[:, 0] + rng.normal(size=64)).astype(np.float32) + model = ob.NaturalBoost(n_trees=2, max_depth=2, subsample=0.5, random_state=42).fit(X, y) + path = tmp_path / "seeded.ob" + model.save(path) + restored = ob.load(path) + assert restored.random_state == 42 + np.testing.assert_array_equal(model.predict(X), restored.predict(X)) + restored.fit(X, y) + np.testing.assert_array_equal(model.predict(X), restored.predict(X)) diff --git a/tests/test_foundation_runner.py b/tests/test_foundation_runner.py new file mode 100644 index 0000000..40e7800 --- /dev/null +++ b/tests/test_foundation_runner.py @@ -0,0 +1,312 @@ +"""The evidence gate must reject apparently successful but incomplete runs.""" + +import json +import subprocess +import sys + +import pytest +from benchmarks.foundation.runner import validate_result + + +@pytest.fixture +def evidence(): + manifest = {"wheel_sha256": "abc", "source_sha": "def", "source_dirty": False} + result = { + "returncode": 0, + "timed_out": False, + "junit": '' + '', + "checks": { + "interop": True, + "native_tree_calls": 4, + "device_objective_calls": 2, + "installed_files_verified": 10, + "dataset_sha256": "123", + }, + "environment": {"cuda_available": True, "gpu_name": "Tesla T4"}, + "wheel_sha256": "abc", + "source_sha": "def", + } + return manifest, result + + +def test_complete_result(evidence): + validate_result(*evidence) + + +@pytest.mark.parametrize( + "fault", + [ + "exit", + "timeout", + "missing", + "skip", + "failure", + "duplicate", + "cuda", + "wheel", + "source", + "fallback", + ], +) +def test_incomplete_result_rejected(evidence, fault): + manifest, result = evidence + if fault == "exit": + result["returncode"] = 1 + elif fault == "timeout": + result["timed_out"] = True + elif fault == "missing": + result["junit"] = "" + elif fault in ("skip", "failure"): + tag = "skipped" if fault == "skip" else "failure" + result["junit"] = result["junit"].replace( + 'name="test_device_interop" />', f'name="test_device_interop"><{tag}/>' + ) + elif fault == "duplicate": + result["junit"] = result["junit"].replace("test_normal_gpu_fit", "test_device_interop") + elif fault == "cuda": + result["environment"]["cuda_available"] = False + elif fault == "wheel": + result["wheel_sha256"] = "wrong" + elif fault == "source": + result["source_sha"] = "wrong" + else: + result["checks"]["device_objective_calls"] = 0 + with pytest.raises(ValueError): + validate_result(manifest, result) + + +def test_cli_returns_nonzero_for_missing_report(tmp_path, evidence): + manifest, _ = evidence + (tmp_path / "manifest.json").write_text(json.dumps(manifest)) + run = subprocess.run( + [sys.executable, "-m", "benchmarks.foundation.runner", str(tmp_path)], + capture_output=True, + text=True, + ) + assert run.returncode != 0 + assert "results.json" in run.stderr + + +def test_correctness_requires_weighted_cases(evidence): + manifest, result = evidence + manifest["suite"] = "correctness" + with pytest.raises(ValueError, match="missing"): + validate_result(manifest, result) + extra = "".join( + f'' + for name in ( + "test_weighted_newton", + "test_weighted_distribution[normal]", + "test_weighted_distribution[poisson]", + ) + ) + result["junit"] = result["junit"].replace("", extra + "") + validate_result(manifest, result) + + +def test_unknown_suite_rejected(evidence): + manifest, result = evidence + manifest["suite"] = "unknown" + with pytest.raises(ValueError, match="Unknown"): + validate_result(manifest, result) + + +def test_boundaries_requires_all_execution_cases(evidence): + manifest, result = evidence + manifest["suite"] = "boundaries" + names = [ + "test_weighted_newton", + "test_weighted_distribution[normal]", + "test_weighted_distribution[poisson]", + "test_visible_fallback[custom]", + "test_visible_fallback[exposure]", + "test_visible_fallback[generic]", + "test_device_error_rolls_back", + "test_device_sampling_preflight[subsample]", + "test_device_sampling_preflight[colsample_bytree]", + "test_eval_callback_persistence[normal]", + "test_eval_callback_persistence[poisson]", + ] + for name in names: + with pytest.raises(ValueError, match="missing"): + validate_result(manifest, result) + result["junit"] = result["junit"].replace( + "", f'' + ) + validate_result(manifest, result) + + +def test_histogram_suite_requires_device_check(evidence): + manifest, result = evidence + manifest["suite"] = "histograms" + result["junit"] = result["junit"].replace( + "", '' + ) + with pytest.raises(ValueError, match="histogram"): + validate_result(manifest, result) + result["checks"]["batch_histograms"] = { + "device_arrays": True, + "legacy_download_wrappers_blocked": True, + "cases": [{}, {}, {}], + } + validate_result(manifest, result) + + +def test_split_suite_requires_routing_evidence(evidence): + manifest, result = evidence + manifest["suite"] = "splits" + result["junit"] = result["junit"].replace( + "", + '', + ) + result["checks"]["batch_histograms"] = { + "device_arrays": True, + "legacy_download_wrappers_blocked": True, + "cases": [{}, {}, {}], + } + with pytest.raises(ValueError, match="split/routing"): + validate_result(manifest, result) + result["checks"]["batch_splits"] = { + "device_arrays": True, + "routed_child_oracle": True, + "exact_ties_and_gain_boundary": True, + "cases": [{}, {}, {}, {}], + } + validate_result(manifest, result) + + +def test_leaf_suite_requires_changed_gradient(evidence): + manifest, result = evidence + manifest["suite"] = "leaves" + extra = "".join( + f'' + for name in ( + "test_batch_histogram_device_oracle", + "test_batch_split_routing_oracle", + "test_batch_leaf_rule_oracle", + ) + ) + result["junit"] = result["junit"].replace("", extra + "") + result["checks"]["batch_histograms"] = { + "device_arrays": True, + "legacy_download_wrappers_blocked": True, + "cases": [{}, {}, {}], + } + result["checks"]["batch_splits"] = { + "device_arrays": True, + "routed_child_oracle": True, + "exact_ties_and_gain_boundary": True, + "cases": [{}, {}, {}, {}], + } + with pytest.raises(ValueError, match="leaf rule"): + validate_result(manifest, result) + result["checks"]["batch_leaves"] = { + "device_arrays": True, + "row_sum_oracle": True, + "bounded_changes_next_gradient": True, + "cases": [{}, {}, {}], + "two_rounds": [{}, {}], + } + validate_result(manifest, result) + + +def test_builder_suite_requires_cache_and_persistence(evidence): + manifest, result = evidence + manifest["suite"] = "builder" + extra = "".join( + f'' + for name in ( + "test_batch_histogram_device_oracle", + "test_batch_split_routing_oracle", + "test_batch_leaf_rule_oracle", + "test_levelwise_builder_device_oracle", + ) + ) + result["junit"] = result["junit"].replace("", extra + "") + result["checks"]["batch_histograms"] = { + "device_arrays": True, + "legacy_download_wrappers_blocked": True, + "cases": [{}, {}, {}], + } + result["checks"]["batch_splits"] = { + "device_arrays": True, + "routed_child_oracle": True, + "exact_ties_and_gain_boundary": True, + "cases": [{}, {}, {}, {}], + } + result["checks"]["batch_leaves"] = { + "device_arrays": True, + "row_sum_oracle": True, + "bounded_changes_next_gradient": True, + "cases": [{}, {}, {}], + "two_rounds": [{}, {}], + } + with pytest.raises(ValueError, match="builder"): + validate_result(manifest, result) + result["checks"]["levelwise_builder"] = { + "device_cache_survives_owner_release": True, + "cpu_load_prediction": True, + "compact_transfer_calls": 85, + "two_channel_cases": [{}, {}, {}, {}], + } + validate_result(manifest, result) + + +def test_trainer_suite_requires_execution_checks(): + # A builder-only artifact cannot satisfy the stronger actual-fit suite. + import json + from pathlib import Path + + from benchmarks.foundation.runner import validate_result + + artifact = ( + Path(__file__).resolve().parents[1] + / "benchmarks/results/foundation/20260905T163823Z-7b16b556" + ) + manifest = json.loads((artifact / "manifest.json").read_text()) + result = json.loads((artifact / "results.json").read_text()) + manifest["suite"] = "trainer" + with pytest.raises(ValueError, match="Required GPU cases"): + validate_result(manifest, result) + + +def test_extension_wheel_uninstall_gate(evidence): + manifest, result = evidence + manifest.update( + suite="extensions", + extension_wheels={"a.whl": "a", "b.whl": "b"}, + extension_sources={"src.py": "c"}, + ) + result["junit"] = result["junit"].replace( + "", '' + ) + result["checks"]["installed_gpu_extensions"] = { + "math_oracle": True, + "clipping_changes_next_gradient": True, + "schedule_changes_prediction": True, + "compact_transfer_calls": 160, + "cases": [ + {"samples": n, "bounded": b, "scheduled": s} + for n in (16, 4097) + for b in (False, True) + for s in (False, True) + ], + "models_saved": 9, + "installed": { + k: {"verified_python_files": 1, "path": "lib/site-packages/" + k} + for k in ("normal_fisher", "bounded_leaves") + }, + "demo": {"device": "cuda"}, + } + with pytest.raises(ValueError, match="conformance"): + validate_result(manifest, result) + result["checks"]["extension_uninstall"] = { + "uninstall_returncode": 0, + "inference_returncode": 0, + "inference_stdout": json.dumps({"extensions_absent": True, "exact_cpu_roundtrips": 9}), + } + validate_result(manifest, result) + result["checks"]["extension_uninstall"]["inference_stdout"] = "{}" + with pytest.raises(ValueError, match="plugin-free"): + validate_result(manifest, result) diff --git a/tests/test_large_scale.py b/tests/test_large_scale.py index d071b12..d9dc040 100644 --- a/tests/test_large_scale.py +++ b/tests/test_large_scale.py @@ -317,6 +317,30 @@ def test_goss_config_validation(self): # Integration Tests with GradientBoosting # ============================================================================= +class TestUnsupportedHighLevelBatching: + """High-level models must not silently ignore ``batch_size``.""" + + @pytest.mark.parametrize( + "model,y", + [ + (ob.GradientBoosting(n_trees=1, batch_size=16), np.arange(64)), + ( + ob.MultiClassGradientBoosting( + n_classes=2, + n_trees=1, + batch_size=16, + ), + np.arange(64) % 2, + ), + ], + ) + def test_batch_size_fails_fast(self, model, y): + X = np.arange(128, dtype=np.float32).reshape(64, 2) + + with pytest.raises(NotImplementedError, match="batch_size"): + model.fit(X, y) + + class TestGOSSIntegration: """Integration tests for GOSS with GradientBoosting.""" diff --git a/tests/test_levelwise_builder.py b/tests/test_levelwise_builder.py new file mode 100644 index 0000000..65b3ffc --- /dev/null +++ b/tests/test_levelwise_builder.py @@ -0,0 +1,183 @@ +"""Whole-tree oracle derives every split and leaf from original rows.""" + +import numpy as np +import pytest + +import openboost as ob +from openboost.experimental import ExecutionContext, LevelWiseBuilder, TrainerConfig + + +def example(): + bins = np.array([[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]], np.uint8) + g = np.array([4, 0, 4, 2, -1, -1, -15, -5], np.float32) + h = np.array([1, 0, 2, 1, 0.5, 1, 3, 1], np.float32) + binned = ob.BinnedArray(bins, [np.array([0.5, 1.5, 2.5]), np.array([0.5])], 2, 8, "cpu") + return binned, g, h + + +def context(xp=np, round_idx=0, channel="a"): + return ExecutionContext( + "cpu" if xp is np else "cuda", xp, np.random.default_rng(7), round_idx, channel + ) + + +def oracle_tree(bins, g, h, config, bound=None): + slots = 2 ** (config.max_depth + 1) - 1 + arrays = { + k: np.full(slots, -1, np.int32) + for k in ("features", "thresholds", "left_children", "right_children") + } + values, predictions = np.zeros(slots, np.float32), np.zeros(len(g), np.float32) + + def grow(node, rows, depth): + G, H = sum(float(v) for v in g[rows]), sum(float(v) for v in h[rows]) + best = None + if depth < config.max_depth and H > 0: + for f in range(len(bins)): + for t in range(255): + left_rows = rows[bins[f, rows] <= t] + right_rows = rows[bins[f, rows] > t] + GL, HL = sum(float(v) for v in g[left_rows]), sum(float(v) for v in h[left_rows]) + GR, HR = sum(float(v) for v in g[right_rows]), sum(float(v) for v in h[right_rows]) + if HL <= 0 or HR <= 0 or min(HL, HR) < config.min_child_weight: + continue + gain = ( + GL**2 / (HL + config.reg_lambda) + + GR**2 / (HR + config.reg_lambda) + - G**2 / (H + config.reg_lambda) + ) + if gain > 0 and gain >= config.min_gain and (best is None or gain > best[0]): + best = (gain, f, t, left_rows, right_rows) + if best is None: + denominator = H + config.reg_lambda + value = -G / denominator if denominator else 0.0 + if bound is not None: + value = np.clip(value, -bound, bound) + values[node], predictions[rows] = value, value + return + _, f, t, left_rows, right_rows = best + arrays["features"][node], arrays["thresholds"][node] = f, t + arrays["left_children"][node], arrays["right_children"][node] = 2 * node + 1, 2 * node + 2 + grow(2 * node + 1, left_rows, depth + 1) + grow(2 * node + 2, right_rows, depth + 1) + + grow(0, np.arange(len(g)), 0) + return {**arrays, "values": values}, predictions + + +@pytest.mark.parametrize("depth", [0, 1, 2, 3]) +def test_whole_tree_row_oracle(depth): + binned, g, h = example() + cfg = TrainerConfig(max_depth=depth) + built = LevelWiseBuilder().build(binned, g, h, config=cfg, context=context()) + arrays, prediction = oracle_tree(binned.data, g, h, cfg) + for k, v in arrays.items(): + np.testing.assert_allclose(getattr(built.tree, k), v, atol=1e-6, rtol=1e-6) + np.testing.assert_array_equal(built.train_prediction, built.tree(binned)) + np.testing.assert_allclose(built.train_prediction, prediction, atol=1e-6, rtol=1e-6) + + +@pytest.mark.parametrize( + "kind", ["missing", "categorical", "subsample", "colsample_bytree", "reg_alpha", "budget"] +) +def test_preflight_before_histogram(monkeypatch, kind): + import openboost.experimental._levelwise as module + + binned, g, h = example() + cfg = TrainerConfig(max_depth=2) + builder = LevelWiseBuilder(memory_budget_bytes=0 if kind == "budget" else 256 * 1024**2) + if kind == "missing": + binned.data[0, 0] = 255 + if kind == "categorical": + binned.is_categorical = np.array([True, False]) + if kind in ("subsample", "colsample_bytree"): + setattr(cfg, kind, 0.5) + if kind == "reg_alpha": + cfg.reg_alpha = 1 + + def forbidden(*args, **kwargs): + raise AssertionError("preflight must precede histograms") + + monkeypatch.setattr(module, "build_histograms", forbidden) + with pytest.raises((ValueError, MemoryError)): + builder.build(binned, g, h, config=cfg, context=context()) + + +def test_two_channel_trainer_schedule_and_persistence(tmp_path): + from openboost.experimental import Booster + from tests.test_batch_leaves import BoundedLeaf + from tests.test_experimental_dispatch import Decay + from tests.test_experimental_objective import TwoSquared + + X = np.arange(16, dtype=np.float32).reshape(8, 2) + y = np.array([3, 3, 2, 2, -1, -1, -4, -4], np.float32) + cfg = TrainerConfig(n_trees=2, max_depth=2, learning_rate=0.5) + models = [] + for rule in (None, BoundedLeaf()): + model = Booster( + objective=TwoSquared(), + tree_builder=LevelWiseBuilder(leaf_rule=rule), + step_schedule=Decay(), + config=cfg, + ).fit(X, y) + before = model.predict_raw(X) + path = tmp_path / f"model{len(models)}.ob" + model.save(path) + with pytest.warns(UserWarning, match="trusted"): + loaded = Booster.load(path) + for channel, raw in before.items(): + np.testing.assert_array_equal(loaded.predict_raw(X)[channel], raw) + assert model.coefficients_ == {"a": [0.5, 0.25], "b": [0.25, 0.125]} + models.append(model) + assert not np.allclose(models[0].predict_raw(X)["a"], models[1].predict_raw(X)["a"]) + + +def test_zero_curvature_and_early_leaf(): + binned, g, h = example() + cfg = TrainerConfig(max_depth=3, reg_lambda=0) + result = LevelWiseBuilder().build( + binned, np.zeros_like(g), np.zeros_like(h), config=cfg, context=context() + ) + assert result.tree.left_children[0] == -1 + np.testing.assert_array_equal(result.train_prediction, 0) + with pytest.raises(ValueError, match="curvature"): + LevelWiseBuilder().build( + binned, np.ones_like(g), np.zeros_like(h), config=cfg, context=context() + ) + + +@pytest.mark.parametrize("early_leaf", [False, True]) +def test_fixed_slot_growth_does_not_compact_split_arrays(monkeypatch, early_leaf): + """Fixed-slot growth must not materialize variable-length masked split arrays.""" + from dataclasses import replace + + import openboost.experimental._levelwise as module + + class FixedSlots(np.ndarray): + def __getitem__(self, index): + if isinstance(index, np.ndarray) and index.dtype == np.bool_: + raise AssertionError("Boolean compaction of fixed-slot split arrays") + return super().__getitem__(index) + + original = module.find_splits + + def splits(*args, **kwargs): + result = original(*args, **kwargs) + return replace( + result, + **{ + name: getattr(result, name).view(FixedSlots) + for name in ("feature", "threshold", "left_child", "right_child", "valid") + }, + ) + + monkeypatch.setattr(module, "find_splits", splits) + binned, g, h = example() + if early_leaf: + g = np.zeros_like(g) + cfg = TrainerConfig(max_depth=3) + built = LevelWiseBuilder().build(binned, g, h, config=cfg, context=context()) + expected, prediction = oracle_tree(binned.data, g, h, cfg) + for name, values in expected.items(): + np.testing.assert_allclose(getattr(built.tree, name), values, rtol=1e-6, atol=1e-6) + np.testing.assert_allclose(built.train_prediction, prediction, rtol=1e-6, atol=1e-6) diff --git a/tests/test_performance_check.py b/tests/test_performance_check.py new file mode 100644 index 0000000..9e54bec --- /dev/null +++ b/tests/test_performance_check.py @@ -0,0 +1,58 @@ +"""Tests for the CI performance comparison harness.""" + +import json +from pathlib import Path + +from benchmarks.check_performance import ( + check_regression, + collect_provenance, + load_baselines, +) + + +def _result(**overrides): + result = { + "fit_time_median": 1.0, + "predict_time_median": 0.1, + "peak_memory_mb": 10.0, + "mse": 0.05, + "r2": 0.95, + "n_samples": 5000, + "n_features": 10, + "n_trees": 100, + "max_depth": 6, + } + result.update(overrides) + return result + + +def test_equal_results_have_no_regression(): + baseline = _result() + + assert check_regression(_result(), baseline) == [] + + +def test_runtime_and_quality_regressions_are_reported(): + baseline = _result() + current = _result(fit_time_median=1.21, mse=0.061) + + regressions = check_regression(current, baseline) + + assert any("fit_time_median" in item for item in regressions) + assert any("mse" in item for item in regressions) + + +def test_load_baselines_uses_explicit_path(tmp_path): + baseline_path = tmp_path / "parent.json" + baseline_path.write_text(json.dumps(_result())) + + assert load_baselines(baseline_path) == _result() + + +def test_provenance_records_source_commit_and_environment(): + provenance = collect_provenance(Path.cwd()) + + assert len(provenance["git_commit"]) == 40 + assert provenance["python_version"] + assert provenance["numpy_version"] + assert provenance["openboost_version"] diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 900e50f..f9f2444 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -73,6 +73,52 @@ def test_save_load_basic(self, regression_data, tmp_path): # Predictions should match np.testing.assert_allclose(pred_before, pred_after, rtol=1e-5) + def test_save_load_preserves_categorical_tree_state(self, tmp_path): + """Categorical split routing survives a model round trip.""" + import openboost as ob + + categories = np.tile( + np.array([0.0, 1.0, 2.0, np.nan], dtype=np.float32), + 60, + ) + X = categories[:, None] + y = np.select( + [categories == 0.0, categories == 1.0, categories == 2.0], + [4.0, -3.0, 2.0], + default=7.0, + ).astype(np.float32) + + X_binned = ob.array(X, categorical_features=[0]) + model = ob.GradientBoosting(n_trees=8, max_depth=2, learning_rate=0.2) + model.fit(X_binned, y) + + assert any( + tree.is_categorical_split is not None + and np.any(tree.is_categorical_split[: tree.n_nodes]) + for tree in model.trees_ + ) + + state = model._to_state_dict() + assert state["_serialization_version"] == 2 + assert all("is_categorical_split" in tree for tree in state["trees_"]) + assert all("cat_bitsets" in tree for tree in state["trees_"]) + + pred_before = model.predict(X) + save_path = tmp_path / "categorical_model.joblib" + model.save(save_path) + loaded = ob.GradientBoosting.load(save_path) + pred_after = loaded.predict(X) + + for expected, actual in zip(model.trees_, loaded.trees_, strict=True): + np.testing.assert_array_equal( + expected.is_categorical_split, + actual.is_categorical_split, + ) + np.testing.assert_array_equal(expected.cat_bitsets, actual.cat_bitsets) + np.testing.assert_array_equal(expected.missing_go_left, actual.missing_go_left) + + np.testing.assert_allclose(pred_before, pred_after, rtol=0, atol=0) + def test_save_load_with_different_losses(self, regression_data, tmp_path): """Test save/load with various loss functions.""" import openboost as ob diff --git a/tests/test_scoringbench_config.py b/tests/test_scoringbench_config.py new file mode 100644 index 0000000..b7b4b7f --- /dev/null +++ b/tests/test_scoringbench_config.py @@ -0,0 +1,145 @@ +"""Launcher parameters must reach model constructors, not only provenance.""" + +import sys +from types import ModuleType + +import pytest +from benchmarks.scoringbench.run import _build_parser, _model_factories +from sklearn.tree import DecisionTreeRegressor + + +@pytest.fixture +def wrappers(monkeypatch): + class Recorded: + def __init__(self, **kwargs): + self.kwargs = kwargs + + for module_name, class_name in ( + ("benchmarks.scoringbench.openboost_wrapper", "OpenBoostWrapper"), + ("scoringbench.wrappers.ngboost_wrapper", "NGBoostWrapper"), + ("scoringbench.wrappers.xgblss_wrapper", "XGBLSSWrapper"), + ("scoringbench.wrappers.catboost_wrapper", "CatBoostQuantileWrapper"), + ): + module = ModuleType(module_name) + setattr(module, class_name, Recorded) + monkeypatch.setitem(sys.modules, module_name, module) + learners = ModuleType("ngboost.learners") + learners.default_tree_learner = DecisionTreeRegressor(max_depth=3, min_samples_leaf=2) + monkeypatch.setitem(sys.modules, "ngboost.learners", learners) + return learners.default_tree_learner + + +def test_declared_seed_and_depth_reach_every_model(wrappers): + args = _build_parser().parse_args( + [ + "--models", + "openboost_cpu,openboost_cuda,ngboost,xgblss,catboost_quantile", + "--seed", + "73", + "--max-depth", + "5", + "--n-trees", + "17", + "--learning-rate", + ".03", + ] + ) + models = {name: make().kwargs for name, make in _model_factories(args).items()} + for name in ("openboost_cpu", "openboost_cuda"): + assert models[name]["model_params"]["random_state"] == 73 + assert models[name]["max_depth"] == 5 + assert models[name]["n_trees"] == 17 + ngb = models["ngboost"]["ngb_params"] + assert ngb["random_state"] == 73 + assert ngb["Base"].max_depth == 5 and ngb["Base"].random_state == 73 + assert ngb["Base"].min_samples_leaf == wrappers.min_samples_leaf + assert ngb["Base"] is not wrappers + assert wrappers.max_depth == 3 and wrappers.random_state is None + assert models["xgblss"]["xgblss_params"]["seed"] == 73 + assert models["xgblss"]["xgblss_params"]["max_depth"] == 5 + assert models["catboost_quantile"]["catboost_params"]["random_seed"] == 73 + assert models["catboost_quantile"]["catboost_params"]["depth"] == 5 + + +def test_ngboost_factories_do_not_share_mutable_base_learner(wrappers): + args = _build_parser().parse_args(["--models", "ngboost"]) + make = _model_factories(args)["ngboost"] + first, second = make().kwargs, make().kwargs + assert first["ngb_params"]["Base"] is not second["ngb_params"]["Base"] + + +def test_configuration_records_base_learner_without_private_fitted_state(): + import json + + from benchmarks.scoringbench.run import _model_configuration + + class Wrapper: + def __init__(self): + self.ngb_params = {"Base": DecisionTreeRegressor(max_depth=5, random_state=73)} + self._model = object() + + recorded = json.loads(json.dumps(_model_configuration(Wrapper()))) + params = recorded["parameters"]["ngb_params"]["Base"]["parameters"] + assert params["max_depth"] == 5 and params["random_state"] == 73 + assert "_model" not in recorded["parameters"] + + +def test_factory_seed_controls_actual_openboost_fit(monkeypatch, wrappers): + """Exercise real wrapper/core fit while isolating optional upstream metrics imports.""" + import importlib.util + from pathlib import Path + + import numpy as np + + base = ModuleType("scoringbench.wrappers.base") + base.DistributionPrediction = object + base.ProbabilisticWrapper = object + quantile = ModuleType("scoringbench.wrappers.quantile_based") + quantile.quantiles_to_distribution = lambda *a, **k: None # not used by fit/predict + monkeypatch.setitem(sys.modules, base.__name__, base) + monkeypatch.setitem(sys.modules, quantile.__name__, quantile) + path = Path(__file__).resolve().parents[1] / "benchmarks/scoringbench/openboost_wrapper.py" + spec = importlib.util.spec_from_file_location("_scoringbench_fit_contract", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + monkeypatch.setitem(sys.modules, "benchmarks.scoringbench.openboost_wrapper", module) + + args = _build_parser().parse_args( + ["--models", "openboost_cpu", "--seed", "73", "--n-trees", "5"] + ) + rng = np.random.default_rng(11) + X = rng.normal(size=(80, 3)).astype(np.float32) + y = (X[:, 0] + rng.normal(size=80)).astype(np.float32) + predictions = [] + global_state = np.random.get_state() + try: + for global_seed in (3, 991): + np.random.seed(global_seed) + model = _model_factories(args)["openboost_cpu"]() + model.model_params["subsample"] = 0.65 # ensure the declared seed is exercised + model.fit(X, y) + assert model._model.random_state == 73 + predictions.append(model.predict(X)) + np.testing.assert_array_equal(*predictions) + args.seed = 74 + other = _model_factories(args)["openboost_cpu"]() + other.model_params["subsample"] = 0.65 + other.fit(X, y) + assert not np.array_equal(other.predict(X), predictions[0]) + finally: + np.random.set_state(global_state) + + +def test_manifest_persists_constructed_configuration(tmp_path, monkeypatch): + import json + + from benchmarks.scoringbench import run + + args = _build_parser().parse_args(["--models", "openboost_cpu", "--seed", "73"]) + configured = {"openboost_cpu": {"parameters": {"model_params": {"random_state": 73}}}} + monkeypatch.setattr(run, "_gpu_info", lambda: None) + monkeypatch.setattr(run, "_git_state", lambda path: {"commit": "test", "dirty": False}) + path = run._write_provenance(tmp_path, tmp_path, args, [], 0, configured) + manifest = json.loads(path.read_text()) + assert manifest["model_parameters"] == configured + assert manifest["arguments"]["seed"] == 73 diff --git a/tests/test_unified_persistence.py b/tests/test_unified_persistence.py new file mode 100644 index 0000000..0b8f912 --- /dev/null +++ b/tests/test_unified_persistence.py @@ -0,0 +1,43 @@ +"""Integration coverage for unified models and the generic model loader.""" + +import numpy as np +import pytest + +import openboost as ob + + +def linear_formula(theta, x): + return theta[0] * x + + +@pytest.mark.parametrize("kind", ["formula", "survival"]) +@pytest.mark.parametrize("mixed_features", [False, True]) +def test_generic_load_unified_model(kind, mixed_features, tmp_path): + rng = np.random.default_rng(31) + X = rng.normal(size=(48, 2)).astype(np.float32) + x = rng.uniform(0.5, 2, len(X)) + y = np.exp(0.2 * X[:, 0]) * x + if mixed_features: + X[:, 1] = np.arange(len(X)) % 3 + X[::7, 0] = np.nan + X = ob.array(X, categorical_features=[1]) + if kind == "formula": + model = ob.FormulaBoost( + formula=linear_formula, n_params=1, links=("log",), + n_trees=2, max_depth=2, + ).fit(X, y, model_input=x) + predict_kwargs = {"model_input": x} + else: + model = ob.WeibullAFT(n_trees=2, max_depth=2).fit( + X, y, event=(np.arange(len(y)) % 4 != 0).astype(float), + ) + predict_kwargs = {} + expected = model.predict(X, **predict_kwargs) + path = tmp_path / f"{kind}.joblib" + model.save(path) + with pytest.warns(UserWarning, match="trusted"): + loaded = ob.load(path) + assert type(loaded) is type(model) + np.testing.assert_array_equal(loaded.predict(X, **predict_kwargs), expected) + for name, values in model.predict_params(X).items(): + np.testing.assert_array_equal(loaded.predict_params(X)[name], values) diff --git a/tests/test_value_protocol.py b/tests/test_value_protocol.py new file mode 100644 index 0000000..fc6fd46 --- /dev/null +++ b/tests/test_value_protocol.py @@ -0,0 +1,132 @@ +import copy + +import pytest +from benchmarks.foundation.value_protocol import CONFIG, STRATEGIES, summarize + + +def fixture(): + metrics = {"nll": 1.0, "crps": 0.4, "coverage90": 0.96} + cells = [ + { + "seed": seed, + "strategy": strategy, + "records": [ + { + "fit_s": 2.0 if strategy == "experimental_cuda" else 1.0, + "predict_s": 0.01, + "metrics": metrics.copy(), + "fallback_warnings": [], + } + for _ in range(4) + ], + } + for seed in (0, 1, 2) + for strategy in STRATEGIES + ] + frozen = [ + { + "seed": s, + "backend": "cuda", + "mode": "resident", + "records": [{}, {"metrics": metrics.copy()}], + } + for s in (0, 1, 2) + ] + return cells, frozen + + +def test_regression_remains_a_reported_result(): + cells, frozen = fixture() + result = summarize(cells, frozen) + assert result["quality_pass"] + assert result["profiling_triggered"] and not result["performance_budget_pass"] + assert result["default_fit_ratio"] == 2 + cells[2]["records"][-1]["metrics"]["crps"] = 0.5 + assert not summarize(cells, frozen)["quality_pass"] + + +def test_incomplete_or_nonfinite_evidence_rejected(): + cells, frozen = fixture() + with pytest.raises(ValueError): + summarize(cells[:-1], frozen) + bad = copy.deepcopy(cells) + bad[0]["records"][0]["fit_s"] = float("nan") + with pytest.raises(ValueError): + summarize(bad, frozen) + + +def test_quality_failure_in_first_fit_is_not_hidden_by_warm_repeats(): + cells, frozen = fixture() + cells[2]["records"][0]["metrics"]["nll"] = 10.0 + assert not summarize(cells, frozen)["quality_pass"] + with pytest.raises(ValueError, match="frozen"): + summarize(cells, []) + + +def test_missing_profile_or_wrong_device_rejected(): + from benchmarks.foundation.value_protocol import validate_profiles + + cells, _ = fixture() + for c in cells: + c.update(config=CONFIG.copy(), mode="resident", split_sizes=[12384, 4128, 4128]) + for r, phase in zip( + c["records"], ("process_first", "warm_1", "warm_2", "warm_3"), strict=True + ): + r["phase"] = phase + with pytest.raises(ValueError, match="profile"): + validate_profiles(cells) + cell = cells[1] + cell["profile"] = { + "wall_s": 1.0, + "top_host_functions": [{}], + "path_functions": [ + dict(file="_tree.py", function="fit_tree_gpu_native", calls=60), + dict(file="_objectives.py", function="step", calls=30), + ], + "memory": { + "errors": [], + "samples": 3, + "initial_used_bytes": 10, + "sampled_peak_used_bytes": 20, + "total_bytes": 100, + "sampled_peak_delta_bytes": 10, + }, + } + for record in cell["records"]: + record["actual_device"] = "cuda" + validate_profiles([cell]) + cell["records"][0]["actual_device"] = "cpu" + with pytest.raises(ValueError, match="device"): + validate_profiles([cell]) + cell["records"][0]["actual_device"] = "cuda" + cell["profile"]["memory"]["sampled_peak_used_bytes"] = 101 + with pytest.raises(ValueError, match="memory"): + validate_profiles([cell]) + + +def test_memory_sampling_runs_after_host_profiler_is_disabled(monkeypatch): + from benchmarks.foundation import value_worker + + seen = [] + + class Model: + def fit(self, X, y): + seen.append("profile_fit") + + def memory(model, X, y, backend, sync): + seen.append("memory_fit") + return {"scope": "test"} + + monkeypatch.setattr(value_worker, "sample_memory_fit", memory) + result = value_worker.profile_fit(Model(), None, None, "cpu", lambda: None) + assert seen == ["profile_fit", "memory_fit"] + assert result["isolated_host_profile"] is True + names = {r["function"] for r in result["top_host_functions"]} + assert "fit" in names and "memory" not in names + + +def test_isolated_profile_matrix_cannot_be_incomplete(): + from benchmarks.foundation.value_protocol import validate_profiles + + with pytest.raises(ValueError, match="Incomplete isolated"): + validate_profiles([], profile_only=True) diff --git a/tests/v1/__init__.py b/tests/v1/__init__.py new file mode 100644 index 0000000..8f83857 --- /dev/null +++ b/tests/v1/__init__.py @@ -0,0 +1 @@ +"""Independent v1 references and, later, production conformance tests.""" diff --git a/tests/v1/reference/README.md b/tests/v1/reference/README.md new file mode 100644 index 0000000..df0bc35 --- /dev/null +++ b/tests/v1/reference/README.md @@ -0,0 +1,114 @@ +# Independent v1 references + +These are small, deliberately slow NumPy oracles for [Sprint 001](../../../v1-sprints/001-scalar-tree-reference.md) +[Sprint 003](../../../v1-sprints/003-data-classification-reference.md), +[Sprint 004](../../../v1-sprints/004-ranking-quantile-vector-reference.md), +[Sprint 005](../../../v1-sprints/005-positive-aft-reference.md), +[Sprint 006](../../../v1-sprints/006-normal-formula-reference.md), +and [Sprint 007](../../../v1-sprints/007-identity-runs-reference.md), +not a production OpenBoost implementation or a performance baseline. + +`scalar.py` implements weighted half-square loss, unweighted derivatives, explicit +weight application, row sums, Newton leaves and half-scaled quadratic improvement. +`tree.py` exhaustively routes original rows for every candidate. It does **not** +call histogram/prefix-sum kernels, production objectives or the existing trainer. +Best-first rescans all leaves; symmetric intersects feasible candidate conditions +and sums layer gains before selecting. This differs structurally from optimized code. + +`tree.py` inputs are fixed numeric bin matrices `[N,F]`, with nonnegative integer-valued codes +and NaN for missing. This tree reference still consumes numeric bins only. +The maximum observed code remains a candidate so observed-vs-missing splits are +possible. Exact ties order feature, threshold, then missing direction (right before +left). Children require positive weighted curvature and the configured minimum; +cohort information is independent of training weights. Empty/illegal candidates +do not split. Default lambda=1, depth=2, eta=.1 and two rounds follow the task cards. + +The tests use hand calculations, fixed counterexamples, integer-weight replication, +two-round traces and an isolated process that blocks every `openboost` import. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache OPENBOOST_BACKEND=cpu uv run --no-sync pytest tests/v1 --confcutdir=tests/v1 -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check tests/v1 +``` + +`data.py` independently fits numeric cuts by direct order-statistic interpolation +and counts crossed cuts by row. Fitted column states are tuples. Missing has a +separate mask; its placeholder code is never a regular value. Category dictionaries +use sorted homogeneous strings or integers (no bool/mixed/floating tokens); unknown +values route as missing. One-vs-rest uses equality, never ordinal thresholds. +An all-missing fitted column has no cuts; validation cannot add cuts. These are +column semantics, not a PreparedData implementation, content hash or row-ID binder. + +`classification.py` keeps class schema separate from feature dictionaries: unknown +or missing labels fail, and training schema needs at least two classes. Binary +base requires both classes and an explicit representable clipping probability. +Signed-margin binary loss avoids cancellation; derivatives remain unweighted. +Softmax returns both the exact full Hessian and the explicitly named diagonal +upper bound. Tests check calculus, class permutations, integer-weight replication, +and two-round joint-snapshot updates through the independent tree reference. + +`ranking.py` enumerates all strict-relevance pairs within each query. Query loss +is the mean over eligible pair count, scaled by query weight; explicit pair weights +scale the numerator, not the count. Row weights fail. Lambda weights are frozen +absolute swap deltas from the current NDCG ranking, with stable row-ID ties; the +returned loss is a surrogate, not NDCG. Zero-IDCG query NDCG is one. + +`quantile.py` supplies pinball loss and pseudo split fields, then replaces terminal +leaves using their original routed residuals/weights. Its weighted quantile uses +the left endpoint convention and ignores zero weights. A ties fixture shows that +a correct leaf solver can coexist with no profitable pseudo split. + +`vector.py` fits a shared stump by summing output split gains; optional projected +gradients and diagonal projected curvatures select topology while leaves always +use full original output statistics. It is a stump probe, not full vector growth. +Target scaling uses train-only unweighted population mean/std and constant flags. + +`positive.py` defines Poisson exposure offsets, Gamma log-mean and fixed-power +Tweedie geometry with explicit support checks. Returned derivatives are unweighted. +Poisson's all-zero base needs an explicit minimum rate; its prediction requires +exposure and distinguishes rate/count. A tiny policy join separates positive-paid +count/mean from raw ClaimNb and reports inconsistent or excluded records. + +`survival.py` accepts exact events and right-censored log-normal intervals only. +It separates event density from survival probability, keeps a stable log-tail and +inverse Mills curvature, and exposes median/mean/survival/quantile mathematics. +The far-tail continued fraction is checked against independent quadrature. These +are numerical fixtures, not clinical evaluation or persistence implementation. + +`coupled.py` defines Normal ordinary/Fisher geometry and Formula Jacobian/GGN, +with ordinary, diagonal and explicit 2x2 full solves. Negative directions are fitted +using scalar trees with training weight exactly once. A small immutable update +record separates joint snapshots, ordered calls, finite fixed steps and strict-loss +backtracking. It is not the public runtime or full transaction/best-state system. +A separate Normal evaluator checks NLL/CRPS; rank-deficiency and misspecification +fixtures prevent interpreting a full solve as proof of parameter identifiability. + +`runs.py` keeps typed content hashes and prepared row IDs, validates role alignment, +and runs independent scalar/vector squared-loss trees sequentially. Stable per-run +random keys, budgets, early stopping, failure records and immutable best terms are +checked at M=1/8/32. Selection rejects different problem identities. Regrouping is +a semantic simulation, not batched execution or a measured cache optimization. + +`author.py` supplies D1 expectile stationary-interval initialization, D3 penalized +pinball breakpoint/stationary-point enumeration and D4's exact six-trial ordered +rule. Row weight mass is not normalized away from the penalty. Tests distinguish +smooth derivative checks from nonsmooth subgradient optimality. These development +oracles do not constitute a public extension or an E5 author-cost result. +See [Sprint 008](../../../v1-sprints/008-author-mutation-reference.md). + +`mixed.py` integrates fitted numeric/category transforms with exhaustive multilevel +scalar/vector trees for depthwise, best-first and symmetric policies. Trees retain +their transform state for raw prediction; split projections preserve full leaf +payloads. Original scalar/stump references remain independent comparisons. This +probe has a depth limit but no leaf-budget option. See [Sprint 009](../../../v1-sprints/009-mixed-vector-growth-reference.md). + +Completed F0.2 reference coverage is tracked in the [acceptance ledger](../../../v1-sprints/f0-2-acceptance-ledger.md). +Production parity, persistence, CUDA and real task evaluation remain pending. + +`integration.py` composes two-round positive-target and three-quantile ensembles, +paid-count/Gamma two-stage predictions, and immutable ordered Normal proposals. +Best restoration restores terms, coefficients and both caches with a fresh version; +foreign/stale proposals, non-finite validation and broadcastable row/shape mismatches +are rejected atomically. Logical-step seed derivation survives retry/restoration. +These finite probes are not a public runtime or persistence implementation. +See [Sprint 010](../../../v1-sprints/010-reference-integration-exit.md); next is F0.3. diff --git a/tests/v1/reference/__init__.py b/tests/v1/reference/__init__.py new file mode 100644 index 0000000..3cb5e72 --- /dev/null +++ b/tests/v1/reference/__init__.py @@ -0,0 +1 @@ +"""Deliberately small, slow NumPy oracles; never import production algorithms.""" diff --git a/tests/v1/reference/author.py b/tests/v1/reference/author.py new file mode 100644 index 0000000..255ce7a --- /dev/null +++ b/tests/v1/reference/author.py @@ -0,0 +1,115 @@ +"""D1/D3/D4 independent author-task oracles, not public extensions or an E5 result.""" + +from dataclasses import replace + +import numpy as np + +from .coupled import normal, step +from .positive import _result +from .quantile import _level, pinball +from .scalar import finite_vector, nonnegative, training_weights +from .tree import fit_tree + + +def expectile(raw, target, *, tau=0.8, weight=None): + tau = _level(tau) + y = finite_vector(target, "target") + raw = finite_vector(raw, "raw", len(y)) + residual = y - raw + asymmetry = np.where(residual < 0, 1 - tau, tau) + # At r=0 the gradient is zero; h=2*tau is a declared branch convention. + return _result(asymmetry * residual**2, -2 * asymmetry * residual, 2 * asymmetry, weight) + + +def expectile_base(target, *, tau=0.8, weight=None): + tau = _level(tau) + y = finite_vector(target, "target") + w = training_weights(weight, len(y)) + y, w = y[w > 0], w[w > 0] + boundaries = sorted(set(y)) + candidates = set(boundaries) + # Each open interval fixes the asymmetric weights. Enumerate its stationary + # point directly; no iteration using the objective gradient is needed. + for low, high in zip(boundaries[:-1], boundaries[1:], strict=True): + mass = w * np.where(y <= low, 1 - tau, tau) + value = sum((mass / sum(mass)) * y) + if low <= value <= high: + candidates.add(float(value)) + + def loss(value): + residual = y - value + return sum(w * np.where(residual < 0, 1 - tau, tau) * residual**2) + + scored = [(loss(value), value) for value in candidates] + if not all(np.isfinite(value) for value, _ in scored): + raise ValueError("expectile base objective exceeds float64") + return float(min(scored)[1]) + + +def penalized_quantile(residual, q, weight=None, *, penalty, anchor): + q = _level(q) + residual = finite_vector(residual, "residual") + w = training_weights(weight, len(residual)) + penalty = nonnegative(penalty, "penalty") + if penalty == 0 or not np.isscalar(anchor) or not np.isfinite(anchor): + raise ValueError("penalty must be positive and anchor finite") + residual, w = residual[w > 0], w[w > 0] + breaks = sorted(set(residual)) + candidates = set(breaks) + boundaries = [-np.inf, *breaks, np.inf] + for low, high in zip(boundaries[:-1], boundaries[1:], strict=True): + left_mass = sum(w[residual <= low]) + value = anchor + (q * sum(w) - left_mass) / penalty + if low <= value <= high and np.isfinite(value): + candidates.add(float(value)) + + def loss(value): + r = residual - value + return sum(w * np.maximum(q * r, (q - 1) * r)) + penalty * (value - anchor) ** 2 / 2 + + values = [(loss(value), value) for value in candidates] + if not all(np.isfinite(loss) for loss, _ in values): + raise ValueError("penalized objective exceeds float64") + return float(min(values)[1]) + + +def penalized_tree(bins, raw, target, q, *, weight=None, penalty, anchor, **tree_options): + _, g, h = pinball(raw, target, q, weight) + residual = np.asarray(target, dtype=float) - np.asarray(raw, dtype=float) + w = training_weights(weight, len(residual)) + # Validate solver config even if no split is chosen. + penalized_quantile(residual, q, w, penalty=penalty, anchor=anchor) + tree = fit_tree(bins, g, h, weight=weight, **tree_options) + nodes = [] + for node in tree.nodes: + if node.condition is None: + rows = list(node.rows) + node = replace( + node, + value=penalized_quantile( + residual[rows], q, w[rows], penalty=penalty, anchor=anchor + ), + ) + nodes.append(node) + return replace(tree, nodes=tuple(nodes)) + + +def ordered_normal(bins, raw, target, *, weight=None, objective=normal): + """D4 exact ordered rule. Rejection passes the unchanged snapshot forward.""" + rates = tuple(0.1 * 0.5**j for j in range(6)) + results = [] + for channel in (0, 1): + result = step( + bins, + raw, + target, + objective, + weight=weight, + mode="full", + channels=(channel,), + rates=rates, + require_decrease=True, + ) + results.append(result) + raw = result.raw_after + return tuple(results) diff --git a/tests/v1/reference/classification.py b/tests/v1/reference/classification.py new file mode 100644 index 0000000..2063032 --- /dev/null +++ b/tests/v1/reference/classification.py @@ -0,0 +1,93 @@ +"""Float64 class mappings and unweighted classification geometry for tiny fixtures.""" + +from dataclasses import dataclass + +import numpy as np + +from .data import CategoryMap +from .scalar import finite_vector, training_weights + + +def _codes(target, length, classes): + y = finite_vector(target, "target", length) + if np.any(y != np.floor(y)) or np.any(y < 0) or np.any(y >= classes): + raise ValueError("target must contain in-range integer class codes") + return y.astype(np.int64) + + +@dataclass(frozen=True) +class ClassMap: + values: tuple[str | int, ...] + + @classmethod + def fit(cls, labels): + labels = tuple(labels) + mapping = CategoryMap.fit(labels) + _, missing = mapping.transform(labels) + if len(mapping.values) < 2 or any(missing): + raise ValueError("training requires at least two classes and no missing labels") + return cls(mapping.values) + + def encode(self, labels): + codes, missing = CategoryMap(self.values).transform(labels) + if any(missing): + raise ValueError("unknown or missing class label") + return codes + + def decode(self, codes): + codes = tuple(codes) + return tuple(self.values[i] for i in _codes(codes, len(codes), len(self.values))) + + +def binary_base(target, *, weight=None, clip): + """Explicit probability clipping; single observed training class is illegal.""" + target = tuple(target) + y = _codes(target, len(target), 2) + if len(set(y)) != 2: + raise ValueError("binary training requires both classes") + if not np.isscalar(clip) or not np.isfinite(clip) or not 0 < clip < 0.5: + raise ValueError("clip must be finite and strictly between zero and one half") + if 1 - clip == 1: + raise ValueError("clip is too small to represent an upper probability below one") + w = training_weights(weight, len(y)) + p = float(np.clip(sum(w * y) / sum(w), clip, 1 - clip)) + return float(np.log(p) - np.log1p(-p)) + + +def binary(raw, target, *, weight=None): + raw = finite_vector(raw, "raw") + y = _codes(target, len(raw), 2) + w = training_weights(weight, len(raw)) + # Use signed-margin loss to avoid subtracting two nearly equal positive terms. + losses = np.logaddexp(0, np.where(y == 1, -raw, raw)) + tail = np.exp(-np.abs(raw)) + p = np.where(raw >= 0, 1 / (1 + tail), tail / (1 + tail)) + gradient = np.where(y == 1, -np.where(raw >= 0, tail / (1 + tail), 1 / (1 + tail)), p) + curvature = tail / (1 + tail) ** 2 + return float(sum((w / sum(w)) * losses)), gradient, curvature + + +def softmax(raw, target, *, weight=None): + """Return loss, probability, gradient, exact Hessian, diagonal UPPER BOUND. + + The bound 2*p*(1-p) is for separate-channel tree fitting, not an exact Hessian. + Every channel is derived from the same raw snapshot; weighting happens later. + """ + raw = np.asarray(raw, dtype=np.float64) + if raw.ndim != 2 or raw.shape[1] < 2 or not np.all(np.isfinite(raw)): + raise ValueError("raw must be a finite [N,K] matrix with K >= 2") + y = _codes(target, len(raw), raw.shape[1]) + w = training_weights(weight, len(raw)) + with np.errstate(over="ignore"): + shifted = raw - np.max(raw, axis=1, keepdims=True) + if not np.all(np.isfinite(shifted)): + raise ValueError("raw dynamic range exceeds float64") + exp = np.exp(shifted) + normalizer = np.sum(exp, axis=1, keepdims=True) + p = exp / normalizer + losses = np.log(normalizer[:, 0]) - shifted[np.arange(len(raw)), y] + gradient = p.copy() + gradient[np.arange(len(raw)), y] -= 1 + exact = np.array([np.diag(row) - np.outer(row, row) for row in p]) + bound = 2 * p * (1 - p) + return float(sum((w / sum(w)) * losses)), p, gradient, exact, bound diff --git a/tests/v1/reference/coupled.py b/tests/v1/reference/coupled.py new file mode 100644 index 0000000..2c2f5bf --- /dev/null +++ b/tests/v1/reference/coupled.py @@ -0,0 +1,210 @@ +"""Tiny Normal/Formula geometry and immutable directional-update probes.""" + +import math +from dataclasses import dataclass + +import numpy as np + +from .positive import _exp, _positive, _result +from .scalar import finite_vector, nonnegative, training_weights, weighted_mean +from .tree import fit_tree, numeric_bins + + +def _raw(values): + raw = np.asarray(values, dtype=float) + if raw.ndim != 2 or raw.shape[1] != 2 or len(raw) == 0 or not np.all(np.isfinite(raw)): + raise ValueError("raw must be a nonempty finite [N,2] matrix") + return raw + + +def normal(raw, target, *, weight=None): + raw = _raw(raw) + y = finite_vector(target, "target", len(raw)) + mu, ell = raw.T + precision = _exp(-2 * ell, "Normal precision") + residual = mu - y + square = residual**2 * precision + loss = ell + square / 2 + 0.5 * math.log(2 * math.pi) + g = np.column_stack((residual * precision, 1 - square)) + fisher = np.zeros((len(raw), 2, 2)) + fisher[:, 0, 0], fisher[:, 1, 1] = precision, 2 + return _result(loss, g, fisher, weight) + + +def normal_base(target, *, minimum_scale, weight=None): + floor = _positive([minimum_scale], "minimum_scale")[0] + y = finite_vector(target, "target") + mu = weighted_mean(y, weight) + w = training_weights(weight, len(y)) + scale = math.sqrt(sum((w / sum(w)) * (y - mu) ** 2)) + if not math.isfinite(scale): + raise ValueError("Normal scale exceeds float64") + return (mu, math.log(max(scale, floor))) + + +def normal_scores(raw, target, *, weight=None): + """Separate evaluator from the objective: direct density and closed-form CRPS.""" + raw = _raw(raw) + y = finite_vector(target, "target", len(raw)) + w = training_weights(weight, len(y)) + nll, crps = [], [] + for (mu, ell), value in zip(raw, y, strict=True): + sigma = float(_exp(ell, "Normal scale")) + z = (value - mu) / sigma + phi = math.exp(-z * z / 2) / math.sqrt(2 * math.pi) + cdf = 0.5 * math.erfc(-z / math.sqrt(2)) + nll.append(math.log(sigma * math.sqrt(2 * math.pi)) + z * z / 2) + crps.append(sigma * (z * (2 * cdf - 1) + 2 * phi - 1 / math.sqrt(math.pi))) + if not np.all(np.isfinite(nll)) or not np.all(np.isfinite(crps)): + raise ValueError("non-finite Normal score") + return float(np.dot(w / sum(w), nll)), float(np.dot(w / sum(w), crps)) + + +def _softplus_inverse(value): + return value + np.log(-np.expm1(-value)) + + +def formula_base(target, *, weight=None): + a = max(weighted_mean(target, weight), 1e-6) + return (float(_softplus_inverse(a)), float(_softplus_inverse(1.0))) + + +def formula_predict(raw, x): + raw = _raw(raw) + x = _positive(x, "structure x", len(raw)) + a, b = np.logaddexp(0, raw).T + if np.any(a <= 0) or np.any(b <= 0): + raise ValueError("formula parameters underflowed outside positive support") + prediction = a * (-np.expm1(-b * x)) + if not np.all(np.isfinite(prediction)): + raise ValueError("non-finite formula prediction") + return prediction, a, b + + +def formula(raw, target, x, *, weight=None): + raw = _raw(raw) + y = finite_vector(target, "target", len(raw)) + x = _positive(x, "structure x", len(raw)) + prediction, a, b = formula_predict(raw, x) + tail = np.exp(-np.abs(raw)) + sigmoid = np.where(raw >= 0, 1 / (1 + tail), tail / (1 + tail)) + jacobian = np.column_stack( + (sigmoid[:, 0] * (-np.expm1(-b * x)), a * x * np.exp(-b * x) * sigmoid[:, 1]) + ) + residual = prediction - y + g = jacobian * residual[:, None] + ggn = np.array([np.outer(row, row) for row in jacobian]) + return _result(residual**2 / 2, g, ggn, weight) + + +def directions(gradient, metric, *, mode="full", damping=0.0): + g = _raw(gradient) + metric = np.asarray(metric, dtype=float) + damping = nonnegative(damping, "damping") + if metric.shape != (len(g), 2, 2) or not np.all(np.isfinite(metric)): + raise ValueError("metric must be finite [N,2,2]") + if mode == "ordinary": + return -g.copy() + if mode == "diagonal": + diagonal = np.diagonal(metric, axis1=1, axis2=2) + damping + if np.any(diagonal <= 0): + raise ValueError("nonpositive diagonal solve") + result = -g / diagonal + elif mode == "full": + result = [] + # Explicit 2x2 inversion, independent of production linear algebra. + for row, m in zip(g, metric, strict=True): + a, b, c, d = m[0, 0] + damping, m[0, 1], m[1, 0], m[1, 1] + damping + determinant = a * d - b * c + if ( + not np.isfinite(determinant) + or b != c + or a <= 0 + or determinant <= np.finfo(float).eps * max(abs(a * d), abs(b * c)) + ): + raise ValueError("metric is not numerically positive definite; specify damping") + result.append( + [(-d * row[0] + b * row[1]) / determinant, (c * row[0] - a * row[1]) / determinant] + ) + result = np.array(result) + else: + raise ValueError("unknown direction mode") + if not np.all(np.isfinite(result)): + raise ValueError("non-finite solve") + return result + + +@dataclass(frozen=True) +class Update: + raw_before: tuple + raw_after: tuple + loss_before: float + loss_after: float + accepted: bool + terms: tuple + trials: tuple + + +def step( + bins, + raw, + target, + objective, + *, + mode="full", + damping=0.0, + weight=None, + channels=(0, 1), + rates=(1.0, 0.5, 0.1), + require_decrease=True, +): + """Fit directions once, try finite coefficients, commit one immutable snapshot. + + Call with one channel then the other for ordered updates. This is a small + reference probe, not the planned public transaction/runtime implementation. + """ + raw = _raw(raw).copy() + bins = numeric_bins(bins) + if len(raw) != len(bins): + raise ValueError("bins/raw alignment mismatch") + channels, rates = tuple(channels), tuple(rates) + if ( + not channels + or len(set(channels)) != len(channels) + or any(c not in (0, 1) for c in channels) + ): + raise ValueError("channels must be a unique nonempty subset of (0,1)") + if not rates or any(not np.isscalar(r) or not np.isfinite(r) or r <= 0 for r in rates): + raise ValueError("rates must be finite positive candidates") + before = tuple(tuple(row) for row in raw) + loss, g, metric = objective(raw, target, weight=weight) + direction = directions(g, metric, mode=mode, damping=damping) + trees = [ + (k, fit_tree(bins, -direction[:, k], np.ones(len(raw)), weight=weight)) for k in channels + ] + delta = np.zeros_like(raw) + for k, tree in trees: + delta[:, k] = tree.predict(bins) + trials = [] + for coefficient in rates: + try: + with np.errstate(over="raise", invalid="raise", divide="raise"): + candidate = raw + coefficient * delta + candidate_loss = objective(candidate, target, weight=weight)[0] + if not np.isfinite(candidate_loss): + raise ValueError("non-finite candidate loss") + except (ValueError, FloatingPointError, OverflowError) as error: + trials.append((float(coefficient), None, type(error).__name__)) + continue + trials.append((float(coefficient), float(candidate_loss), "evaluated")) + if not require_decrease or candidate_loss < loss: + return Update( + before, + tuple(tuple(row) for row in candidate), + loss, + candidate_loss, + True, + tuple((k, tree, float(coefficient)) for k, tree in trees), + tuple(trials), + ) + return Update(before, before, loss, loss, False, (), tuple(trials)) diff --git a/tests/v1/reference/data.py b/tests/v1/reference/data.py new file mode 100644 index 0000000..8d8d0f7 --- /dev/null +++ b/tests/v1/reference/data.py @@ -0,0 +1,88 @@ +"""Immutable column oracles; no production layout, identity or binding machinery.""" + +from dataclasses import dataclass +from numbers import Integral + +import numpy as np + + +def _numeric(values): + result = np.asarray(values, dtype=np.float64) + if result.ndim != 1 or np.any(np.isinf(result)): + raise ValueError("numeric column must be one-dimensional without infinity") + return result + + +@dataclass(frozen=True) +class NumericBinning: + cuts: tuple[float, ...] + + @classmethod + def fit(cls, values, *, bins=254): + column = _numeric(values) + if not len(column): + raise ValueError("training column must not be empty") + if not isinstance(bins, Integral) or isinstance(bins, bool) or bins < 1: + raise ValueError("bins must be a positive integer") + observed = sorted(float(x) for x in column if not np.isnan(x)) + if not observed: + return cls(()) + cuts = set() + # Direct order-statistic interpolation, deliberately no np.quantile call. + for j in range(1, bins): + position = (len(observed) - 1) * j / bins + lo = int(position) + fraction = position - lo + hi = min(lo + 1, len(observed) - 1) + cut = (1 - fraction) * observed[lo] + fraction * observed[hi] + if observed[0] <= cut < observed[-1]: + cuts.add(cut) + return cls(tuple(sorted(cuts))) + + def transform(self, values): + column = _numeric(values) + missing = tuple(bool(np.isnan(x)) for x in column) + codes = tuple( + 0 if m else sum(x > cut for cut in self.cuts) + for x, m in zip(column, missing, strict=True) + ) + return codes, missing + + +def _token(value): + if value is None or isinstance(value, (float, np.floating)) and np.isnan(value): + return None + if isinstance(value, str): + return value + if isinstance(value, Integral) and not isinstance(value, (bool, np.bool_)): + return int(value) + raise ValueError("tokens must be strings or integers; missing is None/NaN") + + +@dataclass(frozen=True) +class CategoryMap: + values: tuple[str | int, ...] + + @classmethod + def fit(cls, values): + tokens = [_token(v) for v in values] + present = [v for v in tokens if v is not None] + if len({type(v) for v in present}) > 1: + raise ValueError("mixed category token types are not supported") + return cls(tuple(sorted(set(present)))) + + def transform(self, values): + tokens = [_token(v) for v in values] + mapping = {v: i for i, v in enumerate(self.values)} + missing = tuple(v not in mapping for v in tokens) + return tuple(mapping.get(v, 0) for v in tokens), missing + + def route(self, values, category, *, missing_left): + category = _token(category) + if category not in self.values or not isinstance(missing_left, bool): + raise ValueError("route needs a fitted category and boolean missing direction") + codes, missing = self.transform(values) + selected = self.values.index(category) + return tuple( + missing_left if m else code == selected for code, m in zip(codes, missing, strict=True) + ) diff --git a/tests/v1/reference/integration.py b/tests/v1/reference/integration.py new file mode 100644 index 0000000..2532f69 --- /dev/null +++ b/tests/v1/reference/integration.py @@ -0,0 +1,197 @@ +"""Finite reference compositions and immutable state; not a public model/runtime API.""" + +from dataclasses import dataclass, replace +from numbers import Integral + +import numpy as np + +from .positive import gamma, gamma_base, poisson, poisson_base, poisson_predict +from .quantile import fit_quantile_tree, weighted_quantile +from .runs import derive_seed +from .tree import fit_tree, numeric_bins + + +@dataclass(frozen=True) +class Ensemble: + base: float + terms: tuple + n_features: int + + def raw(self, values): + x = numeric_bins(values) + if x.shape[1] != self.n_features: + raise ValueError("feature schema mismatch") + raw = np.full(len(x), self.base) + for tree, coefficient in self.terms: + raw += coefficient * tree.predict(x) + if not np.all(np.isfinite(raw)): + raise ValueError("non-finite ensemble prediction") + return raw + + +def fit_positive(bins, target, *, kind, exposure=None, weight=None): + x = numeric_bins(bins) + if kind == "poisson": + base = poisson_base(target, exposure, weight=weight, minimum_rate=1e-8) + + def objective(raw): + return poisson(raw, target, exposure, weight=weight) + elif kind == "gamma": + if exposure is not None: + raise ValueError("Gamma severity does not consume exposure") + base = gamma_base(target, weight=weight) + + def objective(raw): + return gamma(raw, target, weight=weight) + else: + raise ValueError("unknown positive recipe") + raw = np.full(len(x), base) + terms, trace = [], [] + for _ in range(2): + _, g, h = objective(raw) + tree = fit_tree(x, g, h, weight=weight, max_depth=2) + updated = raw + 0.1 * tree.predict(x) + objective(updated) # finite/domain checks before committing + trace.append((tuple(raw), tuple(g), tuple(h), tuple(updated))) + terms.append((tree, 0.1)) + raw = updated + return Ensemble(base, tuple(terms), x.shape[1]), tuple(trace) + + +@dataclass(frozen=True) +class TwoStage: + frequency: Ensemble + severity: Ensemble + + @classmethod + def fit(cls, policy_bins, paid_count, exposure, claim_bins, positive_payments): + frequency, _ = fit_positive(policy_bins, paid_count, kind="poisson", exposure=exposure) + severity, _ = fit_positive(claim_bins, positive_payments, kind="gamma") + return cls(frequency, severity) + + def predict(self, bins, exposure): + frequency = poisson_predict(self.frequency.raw(bins), exposure) + with np.errstate(over="raise", invalid="raise"): + severity = np.exp(self.severity.raw(bins)) + annualized = frequency["rate"] * severity + amount = frequency["count_mean"] * severity + if ( + not np.all(np.isfinite(amount)) + or not np.all(np.isfinite(annualized)) + or np.any(severity <= 0) + ): + raise ValueError("invalid two-stage prediction") + return {"annualized": annualized, "amount": amount} + + +def fit_quantiles(bins, target, *, weight=None): + x = numeric_bins(bins) + models = {} + for q in (0.1, 0.5, 0.9): + base = weighted_quantile(target, q, weight) + raw = np.full(len(x), base) + terms = [] + for _ in range(2): + tree = fit_quantile_tree(x, raw, target, q, weight=weight, max_depth=2) + raw += 0.1 * tree.predict(x) + terms.append((tree, 0.1)) + models[q] = Ensemble(base, tuple(terms), x.shape[1]) + return models + + +def _snapshot(raw): + raw = np.asarray(raw, dtype=float) + if raw.ndim != 2 or min(raw.shape) == 0 or not np.all(np.isfinite(raw)): + raise ValueError("raw must be nonempty finite [N,K]") + return tuple(tuple(row) for row in raw) + + +@dataclass(frozen=True) +class Snapshot: + version: int + step_id: tuple + train_raw: tuple + valid_raw: tuple + terms: tuple + + +@dataclass(frozen=True) +class Track: + run_id: str + seed: int + current: Snapshot + best: Snapshot + best_score: float + + @classmethod + def initialize(cls, run_id, seed, train_raw, valid_raw, *, score): + derive_seed(seed, run_id, 0, "state", "initialize") + train, valid = _snapshot(train_raw), _snapshot(valid_raw) + if len(train[0]) != len(valid[0]): + raise ValueError("output schema mismatch") + metric = float(score(np.array(valid))) + if not np.isfinite(metric): + raise ValueError("non-finite initial metric") + snapshot = Snapshot(0, (0, -1), train, valid, ()) + return cls(run_id, seed, snapshot, snapshot, metric) + + +@dataclass(frozen=True) +class Proposal: + run_id: str + parent_version: int + step_id: tuple + update: object + + +def advance(track, proposal, train_bins, valid_bins, *, score): + current, update = track.current, proposal.update + if proposal.run_id != track.run_id: + raise ValueError("proposal belongs to another run") + if proposal.parent_version != current.version: + raise ValueError("stale parent version") + if update.raw_before != current.train_raw: + raise ValueError("proposal raw does not match accepted parent") + if not update.accepted: + if update.terms or update.raw_after != current.train_raw: + raise ValueError("rejected proposal contains a state change") + return track + step_id = proposal.step_id + if ( + len(step_id) != 2 + or any(not isinstance(i, Integral) or isinstance(i, bool) for i in step_id) + or step_id <= current.step_id + or step_id[0] < 1 + or step_id[1] not in (0, 1) + ): + raise ValueError("invalid logical step order") + train, valid = np.array(current.train_raw), np.array(current.valid_raw) + train_bins, valid_bins = numeric_bins(train_bins), numeric_bins(valid_bins) + if len(train_bins) != len(train) or len(valid_bins) != len(valid): + raise ValueError("proposal data rows do not match state caches") + if np.asarray(update.raw_after).shape != train.shape: + raise ValueError("proposal raw shape differs from state") + for channel, tree, coefficient in update.terms: + if channel not in range(train.shape[1]) or not np.isfinite(coefficient): + raise ValueError("invalid term mapping or coefficient") + train[:, channel] += coefficient * tree.predict(train_bins) + valid[:, channel] += coefficient * tree.predict(valid_bins) + train_snapshot, valid_snapshot = _snapshot(train), _snapshot(valid) + if not np.allclose(train, np.asarray(update.raw_after), rtol=1e-12, atol=1e-12): + raise ValueError("proposal prediction does not match terms") + metric = float(score(valid.copy())) + if not np.isfinite(metric): + raise ValueError("non-finite validation metric") + accepted = Snapshot( + current.version + 1, step_id, train_snapshot, valid_snapshot, current.terms + update.terms + ) + if metric < track.best_score: + return replace(track, current=accepted, best=accepted, best_score=metric) + return replace(track, current=accepted) + + +def restore_best(track): + # Restore the immutable matched payload/cache/step bundle but give it a fresh + # version so a proposal from the abandoned future cannot be replayed. + restored = replace(track.best, version=track.current.version + 1) + return replace(track, current=restored) diff --git a/tests/v1/reference/mixed.py b/tests/v1/reference/mixed.py new file mode 100644 index 0000000..2083f12 --- /dev/null +++ b/tests/v1/reference/mixed.py @@ -0,0 +1,210 @@ +"""Raw mixed-feature and full vector-tree oracle using exhaustive row routing.""" + +from dataclasses import dataclass, replace +from numbers import Integral + +import numpy as np + +from .data import CategoryMap, NumericBinning +from .runs import data_identity +from .scalar import newton_leaf, node_score, nonnegative, training_weights +from .vector import _matrix + + +@dataclass(frozen=True) +class Transformer: + names: tuple + kinds: tuple + encoders: tuple + + @classmethod + def fit(cls, values, *, names, kinds, bins=254): + names, kinds = tuple(names), tuple(kinds) + x = np.asarray(values, dtype=object) + if x.ndim != 2 or min(x.shape) == 0 or x.shape[1] != len(names) or len(names) != len(kinds): + raise ValueError("training shape must match feature schema") + if any(not isinstance(n, str) for n in names) or len(set(names)) != len(names): + raise ValueError("feature names must be unique strings") + if any(kind not in ("numeric", "categorical") for kind in kinds): + raise ValueError("unknown feature kind") + encoders = tuple( + NumericBinning.fit(x[:, f], bins=bins) + if kind == "numeric" + else CategoryMap.fit(x[:, f]) + for f, kind in enumerate(kinds) + ) + return cls(names, kinds, encoders) + + def identity(self, row_ids, values): + self.transform(values) # validate against fitted schema before binding + metadata = { + "version": "mixed-reference-v1", + "kinds": self.kinds, + "encoders": [ + {"cuts": encoder.cuts} if kind == "numeric" else {"categories": encoder.values} + for kind, encoder in zip(self.kinds, self.encoders, strict=True) + ], + } + return data_identity(row_ids, values, self.names, metadata) + + def transform(self, values): + x = np.asarray(values, dtype=object) + if x.ndim != 2 or len(x) == 0 or x.shape[1] != len(self.names): + raise ValueError("prediction shape differs from fitted schema") + result = np.empty(x.shape, dtype=float) + for f, encoder in enumerate(self.encoders): + codes, missing = encoder.transform(x[:, f]) + result[:, f] = [np.nan if m else c for c, m in zip(codes, missing, strict=True)] + return result + + +def _route(x, rows, condition, kinds): + feature, code, missing_left = condition + left, right = [], [] + for row in rows: + value = x[row, feature] + selected = ( + missing_left + if np.isnan(value) + else (value == code if kinds[feature] == "categorical" else value <= code) + ) + (left if selected else right).append(row) + return tuple(left), tuple(right) + + +@dataclass(frozen=True) +class Node: + rows: tuple + depth: int + value: tuple + condition: tuple | None = None + left: int = -1 + right: int = -1 + + +@dataclass(frozen=True) +class MixedTree: + transformer: Transformer + nodes: tuple + + def predict(self, values): + x = self.transformer.transform(values) + output = [] + for row in range(len(x)): + node = self.nodes[0] + while node.condition is not None: + left, _ = _route(x, (row,), node.condition, self.transformer.kinds) + node = self.nodes[node.left if left else node.right] + output.append(node.value) + return np.array(output) + + +def grow( + values, + gradient, + curvature, + transformer, + *, + weight=None, + projection=None, + max_depth=2, + policy="depthwise", + reg_lambda=1.0, +): + x = transformer.transform(values) + g, h = _matrix(gradient, "gradient"), _matrix(curvature, "curvature") + if g.shape != h.shape or len(g) != len(x) or np.any(h < 0): + raise ValueError("aligned gradient and nonnegative curvature required") + if not isinstance(max_depth, Integral) or isinstance(max_depth, bool) or max_depth < 0: + raise ValueError("max_depth must be a nonnegative integer") + if policy not in ("depthwise", "best_first", "symmetric"): + raise ValueError("unknown growth policy") + reg_lambda = nonnegative(reg_lambda, "reg_lambda") + w = training_weights(weight, len(x)) + p = np.eye(g.shape[1]) if projection is None else _matrix(projection, "projection") + if p.shape[0] != g.shape[1] or np.any(np.sum(p * p, axis=0) == 0): + raise ValueError("projection requires K rows and nonzero columns") + sg, sh = (g @ p) * w[:, None], (h @ (p * p)) * w[:, None] + lg, lh = g * w[:, None], h * w[:, None] + if not all(np.all(np.isfinite(a)) for a in (sg, sh, lg, lh)): + raise ValueError("non-finite statistics") + conditions = tuple( + (f, int(code), missing_left) + for f in range(x.shape[1]) + for code in sorted(set(x[~np.isnan(x[:, f]), f])) + for missing_left in (False, True) + ) + + def sums(fields, rows): + return np.array([sum(fields[i, k] for i in rows) for k in range(fields.shape[1])]) + + def score(rows): + gs, hs = sums(sg, rows), sums(sh, rows) + result = sum(node_score(a, b, reg_lambda=reg_lambda) for a, b in zip(gs, hs, strict=True)) + if not np.isfinite(result): + raise ValueError("non-finite vector score") + return result + + def leaf(rows, depth): + gs, hs = sums(lg, rows), sums(lh, rows) + return Node( + rows, + depth, + tuple(newton_leaf(a, b, reg_lambda=reg_lambda) for a, b in zip(gs, hs, strict=True)), + ) + + def candidates(node): + parent = score(node.rows) + choices = {} + for condition in conditions: + left, right = _route(x, node.rows, condition, transformer.kinds) + if not left or not right or np.any(sums(sh, left) <= 0) or np.any(sums(sh, right) <= 0): + continue + gain = score(left) + score(right) - parent + if not np.isfinite(gain): + raise ValueError("non-finite candidate gain") + choices[condition] = (gain, left, right) + return choices + + nodes = [leaf(tuple(range(len(x))), 0)] + leaves = {0} + while True: + active = sorted(i for i in leaves if nodes[i].depth < max_depth) + if not active: + break + by_node = {i: candidates(nodes[i]) for i in active} + if policy == "symmetric": + common = set.intersection(*(set(by_node[i]) for i in active)) + if not common: + break + totals = {c: sum(by_node[i][c][0] for i in active) for c in common} + if not all(np.isfinite(value) for value in totals.values()): + raise ValueError("non-finite symmetric layer gain") + condition = min(common, key=lambda c: (-totals[c], c)) + if totals[condition] <= 0: + break + chosen = [(i, condition) for i in active] + else: + options = [] + for i in active: + legal = [c for c in by_node[i] if by_node[i][c][0] > 0] + if legal: + condition = min(legal, key=lambda c: (-by_node[i][c][0], c)) + options.append((i, condition)) + if not options: + break + if policy == "best_first": + chosen = [ + min(options, key=lambda pair: (-by_node[pair[0]][pair[1]][0], pair[0], pair[1])) + ] + else: + depth = min(nodes[i].depth for i, _ in options) + chosen = [(i, c) for i, c in options if nodes[i].depth == depth] + for i, condition in sorted(chosen): + _, left, right = by_node[i][condition] + first = len(nodes) + nodes[i] = replace(nodes[i], condition=condition, left=first, right=first + 1) + nodes.extend((leaf(left, nodes[i].depth + 1), leaf(right, nodes[i].depth + 1))) + leaves.remove(i) + leaves.update((first, first + 1)) + return MixedTree(transformer, tuple(nodes)) diff --git a/tests/v1/reference/positive.py b/tests/v1/reference/positive.py new file mode 100644 index 0000000..3c9cfa0 --- /dev/null +++ b/tests/v1/reference/positive.py @@ -0,0 +1,148 @@ +"""Count/positive-target formulas and tiny policy joins; no production model API.""" + +import math + +import numpy as np + +from .scalar import finite_vector, training_weights + + +def _positive(values, name, length=None): + values = finite_vector(values, name, length) + if np.any(values <= 0): + raise ValueError(f"{name} must be strictly positive") + return values + + +def _target(values, *, strictly_positive=False, count=False): + y = finite_vector(values, "target") + if np.any(y <= 0 if strictly_positive else y < 0) or count and np.any(y != np.floor(y)): + raise ValueError("invalid target support") + return y + + +def _exp(values, name): + with np.errstate(over="ignore", under="ignore"): + result = np.exp(values) + if not np.all(np.isfinite(result)) or np.any(result <= 0): + raise ValueError(f"{name} exponential is outside positive float64 range") + return result + + +def _result(loss, g, h, weight): + if not all(np.all(np.isfinite(v)) for v in (loss, g, h)): + raise ValueError("non-finite objective geometry") + w = training_weights(weight, len(g)) + aggregate = float(sum((w / sum(w)) * loss)) + if not math.isfinite(aggregate): + raise ValueError("non-finite weighted objective") + return aggregate, g, h + + +def poisson(raw, target, exposure, *, weight=None): + y = _target(target, count=True) + f = finite_vector(raw, "raw", len(y)) + e = _positive(exposure, "exposure", len(y)) + log_mu = f + np.log(e) + mu = _exp(log_mu, "count mean") + loss = mu - y * log_mu + np.array([math.lgamma(v + 1) for v in y]) + return _result(loss, mu - y, mu, weight) + + +def poisson_base(target, exposure, *, weight=None, minimum_rate): + y = _target(target, count=True) + e = _positive(exposure, "exposure", len(y)) + w = training_weights(weight, len(y)) + if not np.isscalar(minimum_rate) or not np.isfinite(minimum_rate) or minimum_rate <= 0: + raise ValueError("minimum_rate must be finite and positive") + numerator, denominator = sum(w * y), sum(w * e) + if not np.isfinite(numerator) or not np.isfinite(denominator) or denominator <= 0: + raise ValueError("invalid rate totals") + if numerator == 0: + return math.log(minimum_rate) + return math.log(numerator) - math.log(denominator) + + +def gamma(raw, target, *, weight=None): + y = _target(target, strictly_positive=True) + f = finite_vector(raw, "raw", len(y)) + ratio = _exp(np.log(y) - f, "Gamma target/mean ratio") + return _result(ratio + f, 1 - ratio, ratio, weight) + + +def tweedie(raw, target, *, power=1.5, weight=None): + if not np.isscalar(power) or not np.isfinite(power) or not 1 < power < 2: + raise ValueError("power must be finite and strictly between one and two") + y = _target(target) + f = finite_vector(raw, "raw", len(y)) + a = _exp((2 - power) * f, "Tweedie mean power") + b = np.zeros(len(y)) + positive = y > 0 + b[positive] = _exp(np.log(y[positive]) + (1 - power) * f[positive], "Tweedie target term") + return _result( + b / (power - 1) + a / (2 - power), a - b, (2 - power) * a + (power - 1) * b, weight + ) + + +def policy_losses(policies, payments): + """Return sorted (ID,e,total,annualized,paid_count,paid_mean) plus exclusions. + + Fixture IDs are strings; raw ClaimNb is only a consistency signal. It is + never substituted for the count of positive payment records. + """ + table, paid, excluded = {}, {}, [] + for policy, count, exposure in policies: + if not isinstance(policy, str) or policy in table: + raise ValueError("policy IDs must be unique strings") + _target([count], count=True) + _positive([exposure], "exposure") + table[policy] = (count, exposure) + paid[policy] = [] + for policy, amount in payments: + if not np.isscalar(amount) or not np.isfinite(amount): + raise ValueError("payment must be finite") + if policy not in table: + excluded.append((policy, "orphan_payment")) + elif amount <= 0: + excluded.append((policy, "nonpositive_payment")) + else: + paid[policy].append(float(amount)) + rows = [] + for policy in sorted(table): + count, exposure = table[policy] + values = paid[policy] + if count > 0 and not values: + excluded.append((policy, "count_without_payment")) + elif count == 0 and values: + excluded.append((policy, "payment_without_count")) + else: + total = math.fsum(values) + annualized = total / exposure + if not np.isfinite(total) or not np.isfinite(annualized): + raise ValueError("policy total exceeds float64") + rows.append( + ( + policy, + exposure, + total, + annualized, + len(values), + total / len(values) if values else 0.0, + ) + ) + return tuple(rows), tuple(excluded) + + +def poisson_predict(raw, exposure): + f = finite_vector(raw, "raw") + e = _positive(exposure, "exposure", len(f)) + return {"rate": _exp(f, "rate"), "count_mean": _exp(f + np.log(e), "count mean")} + + +def gamma_base(target, *, weight=None): + y = _target(target, strictly_positive=True) + w = training_weights(weight, len(y)) + mean = sum((w / sum(w)) * y) + if not np.isfinite(mean) or mean <= 0: + raise ValueError("invalid Gamma mean") + return math.log(mean) diff --git a/tests/v1/reference/quantile.py b/tests/v1/reference/quantile.py new file mode 100644 index 0000000..ec4f596 --- /dev/null +++ b/tests/v1/reference/quantile.py @@ -0,0 +1,54 @@ +"""Pinball geometry and routed residual quantile leaves, independent of production.""" + +from dataclasses import replace + +import numpy as np + +from .scalar import finite_vector, training_weights +from .tree import fit_tree + + +def _level(q): + if not np.isscalar(q) or not np.isfinite(q) or not 0 < q < 1: + raise ValueError("q must be finite and strictly between zero and one") + return float(q) + + +def weighted_quantile(values, q, weight=None): + q = _level(q) + values = finite_vector(values, "values") + w = training_weights(weight, len(values)) + ordered = sorted((float(v), float(m)) for v, m in zip(values, w, strict=True) if m > 0) + threshold = q * sum(m for _, m in ordered) + cumulative = 0.0 + for value, mass in ordered: + cumulative += mass + if cumulative >= threshold: + return value + return ordered[-1][0] # floating summation endpoint + + +def pinball(raw, target, q, weight=None): + q = _level(q) + y = finite_vector(target, "target") + raw = finite_vector(raw, "raw", len(y)) + w = training_weights(weight, len(y)) + residual = y - raw + if not np.all(np.isfinite(residual)): + raise ValueError("non-finite residual") + loss = np.maximum(q * residual, (q - 1) * residual) + return float(sum((w / sum(w)) * loss)), (y < raw).astype(float) - q, np.ones(len(y)) + + +def fit_quantile_tree(bins, raw, target, q, *, weight=None, **tree_options): + _, g, pseudo_h = pinball(raw, target, q, weight) + tree = fit_tree(bins, g, pseudo_h, weight=weight, **tree_options) + residual = np.asarray(target, dtype=float) - np.asarray(raw, dtype=float) + w = training_weights(weight, len(residual)) + nodes = [] + for node in tree.nodes: + if node.condition is None: + rows = list(node.rows) + node = replace(node, value=weighted_quantile(residual[rows], q, w[rows])) + nodes.append(node) + return replace(tree, nodes=tuple(nodes)) diff --git a/tests/v1/reference/ranking.py b/tests/v1/reference/ranking.py new file mode 100644 index 0000000..70466f7 --- /dev/null +++ b/tests/v1/reference/ranking.py @@ -0,0 +1,123 @@ +"""All-pairs query-local logistic oracle; lambda weights are frozen, not differentiated.""" + +from dataclasses import dataclass +from numbers import Integral + +import numpy as np + +from .scalar import finite_vector + + +def _input(scores, relevance, row_ids, k): + s = finite_vector(scores, "scores") + rel = finite_vector(relevance, "relevance", len(s)) + if np.any(rel < 0) or np.any(rel != np.floor(rel)): + raise ValueError("relevance must contain nonnegative integers") + if not isinstance(k, Integral) or isinstance(k, bool) or k < 1: + raise ValueError("k must be a positive integer") + ids = tuple(range(len(s))) if row_ids is None else tuple(row_ids) + if ( + len(ids) != len(s) + or any(not isinstance(i, Integral) or isinstance(i, bool) for i in ids) + or len(set(ids)) != len(ids) + ): + raise ValueError("row_ids must be aligned unique integers") + with np.errstate(over="ignore"): + gains = np.exp2(rel) - 1 + if not np.all(np.isfinite(gains)): + raise ValueError("relevance gains exceed float64") + return s, rel, ids, gains + + +def _discount(rank, k): + return 1 / np.log2(rank + 2) if rank < k else 0.0 + + +def _ideal(gains, k): + value = sum(g * _discount(i, k) for i, g in enumerate(sorted(gains, reverse=True))) + if not np.isfinite(value): + raise ValueError("ideal DCG exceeds float64") + return value + + +def query_ndcg(scores, relevance, *, row_ids=None, k=10): + s, _, ids, gains = _input(scores, relevance, row_ids, k) + ideal = _ideal(gains, k) + if ideal == 0: + return 1.0 + order = sorted(range(len(s)), key=lambda i: (-s[i], ids[i])) + return float(sum(gains[i] * _discount(rank, k) for rank, i in enumerate(order)) / ideal) + + +@dataclass(frozen=True) +class PairResult: + loss: float + gradient: np.ndarray + curvature: np.ndarray + pairs: tuple[tuple[int, int, float], ...] # row positions and effective frozen weight + + +def pairwise( + scores, + relevance, + query, + *, + row_ids=None, + query_weight=None, + pair_weight=None, + weight=None, + lambdas=False, + k=10, +): + if weight is not None: + raise ValueError("ranking requires explicit query/pair weights, not row weight") + s, rel, ids, gains = _input(scores, relevance, row_ids, k) + groups = tuple(query) + if len(groups) != len(s) or not groups: + raise ValueError("query must align with nonempty scores") + if any(not isinstance(q, (str, Integral)) or isinstance(q, bool) for q in groups): + raise ValueError("query IDs must be strings or integers") + grouped = {q: [i for i, g in enumerate(groups) if g == q] for q in dict.fromkeys(groups)} + qw = {} if query_weight is None else dict(query_weight) + pw = {} if pair_weight is None else dict(pair_weight) + all_pairs = {(i, j) for rows in grouped.values() for i in rows for j in rows if rel[i] > rel[j]} + if not set(qw) <= set(grouped) or not set(pw) <= all_pairs: + raise ValueError( + "weight keys must identify existing queries or eligible row-position pairs" + ) + if any(not np.isscalar(w) or not np.isfinite(w) or w < 0 for w in (*qw.values(), *pw.values())): + raise ValueError("weights must be finite and nonnegative") + g, h = np.zeros(len(s)), np.zeros(len(s)) + loss, trace = 0.0, [] + for q, rows in grouped.items(): + pairs = [(i, j) for i in rows for j in rows if rel[i] > rel[j]] + order = sorted(rows, key=lambda i: (-s[i], ids[i])) + rank = {i: r for r, i in enumerate(order)} + ideal = _ideal(gains[rows], k) + for i, j in pairs: + factor = qw.get(q, 1.0) * pw.get((i, j), 1.0) / len(pairs) + if lambdas: + delta = ( + 0.0 + if ideal == 0 + else abs( + (gains[i] - gains[j]) * (_discount(rank[i], k) - _discount(rank[j], k)) + ) + / ideal + ) + factor *= delta + difference = float(s[i]) - float(s[j]) + if not np.isfinite(difference): + raise ValueError("score difference exceeds float64") + tail = np.exp(-abs(difference)) + prob = tail / (1 + tail) if difference >= 0 else 1 / (1 + tail) + curvature = tail / (1 + tail) ** 2 + loss += factor * np.logaddexp(0, -difference) + g[i] -= factor * prob + g[j] += factor * prob + h[i] += factor * curvature + h[j] += factor * curvature + trace.append((i, j, float(factor))) + if not np.isfinite(loss) or not np.all(np.isfinite(g)) or not np.all(np.isfinite(h)): + raise ValueError("non-finite pair reduction") + return PairResult(float(loss), g, h, tuple(trace)) diff --git a/tests/v1/reference/runs.py b/tests/v1/reference/runs.py new file mode 100644 index 0000000..2d34f9f --- /dev/null +++ b/tests/v1/reference/runs.py @@ -0,0 +1,268 @@ +"""Typed content identity and sequential run oracle; no production scheduler/cache.""" + +import hashlib +import json +import math +from dataclasses import dataclass +from numbers import Integral + +import numpy as np + +from .tree import fit_tree, numeric_bins + + +def _canonical(value): + if isinstance(value, np.ndarray): + return _canonical(value.tolist()) + if isinstance(value, np.generic): + return _canonical(value.item()) + if value is None: + return ["null"] + if isinstance(value, bool): + return ["bool", value] + if isinstance(value, int): + return ["int", str(value)] + if isinstance(value, float): + return ["float", value.hex() if not math.isnan(value) else "nan"] + if isinstance(value, str): + return ["str", value] + if isinstance(value, (list, tuple)): + return ["sequence", [_canonical(v) for v in value]] + if isinstance(value, dict) and all(isinstance(k, str) for k in value): + return ["mapping", [[k, _canonical(value[k])] for k in sorted(value)]] + raise ValueError("identity only accepts explicitly typed primitive data") + + +def _digest(value): + payload = json.dumps(_canonical(value), ensure_ascii=False, separators=(",", ":")).encode( + "utf-8" + ) + return hashlib.sha256(payload).hexdigest() + + +def _ids(values): + ids = tuple(values) + if ( + not ids + or any(not isinstance(i, Integral) or isinstance(i, (bool, np.bool_)) for i in ids) + or len(set(ids)) != len(ids) + ): + raise ValueError("row IDs must be nonempty unique integers") + return ids + + +@dataclass(frozen=True) +class DataIdentity: + digest: str + row_ids: tuple + + +def data_identity(row_ids, values, schema, transformer): + ids = _ids(row_ids) + matrix = np.asarray(values, dtype=object) + schema = tuple(schema) + if matrix.ndim != 2 or matrix.shape != (len(ids), len(schema)) or not schema: + raise ValueError("data shape must match row IDs and schema") + if any(not isinstance(s, str) for s in schema) or len(set(schema)) != len(schema): + raise ValueError("feature names must be unique strings") + return DataIdentity(_digest(("data-v1-reference", ids, matrix, schema, transformer)), ids) + + +def bind_identity(prepared, row_ids, **fields): + ids = _ids(row_ids) + if not isinstance(prepared, DataIdentity) or ids != prepared.row_ids or "target" not in fields: + raise ValueError("binding requires prepared row IDs and a target role") + payload = {} + for name, (field_ids, values) in fields.items(): + if _ids(field_ids) != ids or len(values) != len(ids): + raise ValueError(f"{name} row IDs or length do not match prepared order") + payload[name] = values + # This hashes role contents; objective-specific support validation is separate. + return _digest(("problem-v1-reference", prepared.digest, ids, payload)) + + +def derive_seed(seed, run_id, round_index, component, purpose): + if any( + not isinstance(v, Integral) or isinstance(v, bool) or v < 0 for v in (seed, round_index) + ): + raise ValueError("seed and round must be nonnegative integers") + if any(not isinstance(v, str) or not v for v in (run_id, component, purpose)): + raise ValueError("RNG identifiers must be nonempty strings") + return int( + _digest(("rng-v1-reference", int(seed), run_id, int(round_index), component, purpose))[:16], + 16, + ) + + +@dataclass(frozen=True) +class RunSpec: + run_id: str + seed: int + budget: int + learning_rate: float + patience: int + sample_count: int + fail_round: int | None = None + + +@dataclass(frozen=True) +class RunResult: + run_id: str + status: str + rounds: int + best_round: int + best_loss: float + base: tuple + best_raw: tuple + best_terms: tuple + samples: tuple + learning_rate: float + problem_id: str + error: str | None + + +def _target(values, length): + y = np.asarray(values, dtype=float) + if y.ndim != 2 or y.shape[0] != length or y.shape[1] < 1 or not np.all(np.isfinite(y)): + raise ValueError("target must be finite [N,K]") + return y.copy() + + +def _snapshot(values): + return tuple(tuple(row) for row in values) + + +def _one(spec, train, validation, problems): + rounds, best_round, best_loss = 0, 0, float("inf") + base, best_raw, best_terms, samples = (), (), (), [] + problem_id = "" + status, error = "completed", None + try: + for name, value, minimum in ( + ("budget", spec.budget, 0), + ("patience", spec.patience, 1), + ("sample_count", spec.sample_count, 1), + ): + if not isinstance(value, Integral) or isinstance(value, bool) or value < minimum: + raise ValueError(f"invalid {name}") + if spec.fail_round is not None and ( + not isinstance(spec.fail_round, Integral) + or isinstance(spec.fail_round, bool) + or not 1 <= spec.fail_round <= spec.budget + ): + raise ValueError("fail_round must identify a budgeted round") + if ( + spec.sample_count > len(train) + or not np.isfinite(spec.learning_rate) + or spec.learning_rate < 0 + ): + raise ValueError("invalid sampling or learning rate") + derive_seed(spec.seed, spec.run_id, 0, "tree", "rows") + if spec.run_id not in problems: + raise ValueError("missing run problem") + target, valid_target = problems[spec.run_id] + target, valid_target = _target(target, len(train)), _target(valid_target, len(validation)) + if target.shape[1] != valid_target.shape[1]: + raise ValueError("validation output schema mismatch") + identities = [] + for features, labels in ((train, target), (validation, valid_target)): + rows = tuple(range(len(features))) + prepared = data_identity( + rows, + features, + [f"f{i}" for i in range(features.shape[1])], + {"encoding": "fixed-bins-reference-v1"}, + ) + identities.append(bind_identity(prepared, rows, target=(rows, labels))) + problem_id = _digest(("squared-run-problem", identities)) + base = tuple(np.mean(target, axis=0)) + raw = np.tile(base, (len(train), 1)) + valid_raw = np.tile(base, (len(validation), 1)) + best_loss = float(np.mean((valid_raw - valid_target) ** 2) / 2) + if not np.isfinite(best_loss): + raise ValueError("non-finite initial validation metric") + best_raw = _snapshot(valid_raw) + terms, stale = [], 0 + for iteration in range(1, spec.budget + 1): + if iteration == spec.fail_round: + raise RuntimeError(f"injected failure at round {iteration}") + seed = derive_seed(spec.seed, spec.run_id, iteration, "tree", "rows") + rows = tuple( + sorted( + int(i) + for i in np.random.default_rng(seed).choice( + len(train), spec.sample_count, replace=False + ) + ) + ) + chosen = list(rows) + gradient = raw - target + trees = tuple( + fit_tree(train[chosen], gradient[chosen, k], np.ones(len(chosen)), max_depth=1) + for k in range(target.shape[1]) + ) + updated = raw + spec.learning_rate * np.column_stack([t.predict(train) for t in trees]) + valid_updated = valid_raw + spec.learning_rate * np.column_stack( + [t.predict(validation) for t in trees] + ) + score = float(np.mean((valid_updated - valid_target) ** 2) / 2) + if not np.all(np.isfinite(updated)) or not np.isfinite(score): + raise ValueError("non-finite candidate state") + # All output channels commit together after candidate validation. + raw, valid_raw = updated, valid_updated + rounds = iteration + samples.append(rows) + terms.append(trees) + if score < best_loss: + best_loss, best_round, best_raw, best_terms = ( + score, + iteration, + _snapshot(valid_raw), + tuple(terms), + ) + stale = 0 + else: + stale += 1 + if stale >= spec.patience: + status = "early_stopped" + break + except (ValueError, RuntimeError, FloatingPointError, OverflowError) as failure: + status, error = "failed", f"{type(failure).__name__}: {failure}" + return RunResult( + spec.run_id, + status, + rounds, + best_round, + best_loss, + base, + best_raw, + best_terms, + tuple(samples), + spec.learning_rate, + problem_id, + error, + ) + + +def run_many(specs, train_bins, validation_bins, problems): + specs = tuple(specs) + ids = [s.run_id for s in specs] + if any(not isinstance(i, str) or not i for i in ids) or len(set(ids)) != len(ids): + raise ValueError("run IDs must be unique nonempty strings") + train, validation = numeric_bins(train_bins), numeric_bins(validation_bins) + if train.shape[1] != validation.shape[1]: + raise ValueError("train/validation feature schema mismatch") + return {spec.run_id: _one(spec, train, validation, problems) for spec in specs} + + +def select_best(records): + candidates = [ + r + for r in records.values() + if r.status in ("completed", "early_stopped") and np.isfinite(r.best_loss) + ] + if not candidates: + raise ValueError("no successful run can be selected") + if len({r.problem_id for r in candidates}) != 1: + raise ValueError("model selection requires the same problem and validation metric") + return min(candidates, key=lambda r: (r.best_loss, r.run_id)) diff --git a/tests/v1/reference/scalar.py b/tests/v1/reference/scalar.py new file mode 100644 index 0000000..d00a1b5 --- /dev/null +++ b/tests/v1/reference/scalar.py @@ -0,0 +1,105 @@ +"""Float64 scalar mathematics for tiny v1 fixtures, not a production backend.""" + +from numbers import Integral + +import numpy as np + + +def finite_vector(values, name, length=None): + result = np.asarray(values, dtype=np.float64) + if result.ndim != 1 or not np.all(np.isfinite(result)): + raise ValueError(f"{name} must be a finite vector") + if length is not None and len(result) != length: + raise ValueError(f"{name} must have length {length}") + return result + + +def training_weights(weight, length): + values = ( + np.ones(length, dtype=np.float64) + if weight is None + else finite_vector(weight, "weight", length) + ) + total = float(np.sum(values)) + if np.any(values < 0) or not np.isfinite(total) or total <= 0: + raise ValueError("weight must be nonnegative with finite positive total") + return values + + +def row_ids(rows, length): + result = tuple(rows) + if any( + not isinstance(i, Integral) or isinstance(i, bool) or i < 0 or i >= length for i in result + ) or len(set(result)) != len(result): + raise ValueError("rows must contain unique in-range integer indices") + return tuple(sorted(int(i) for i in result)) + + +def sum_rows(fields, rows): + """Explicit row-wise addition in original row order, including empty sets.""" + values = np.asarray(fields, dtype=np.float64) + if values.ndim != 2 or not np.all(np.isfinite(values)): + raise ValueError("fields must be a finite matrix") + total = np.zeros(values.shape[1], dtype=np.float64) + for row in row_ids(rows, len(values)): + total += values[row] + if not np.all(np.isfinite(total)): + raise ValueError("non-finite row reduction") + return total + + +def weighted_mean(y, weight=None): + target = finite_vector(y, "target") + w = training_weights(weight, len(target)) + weighted_y, total_w = sum_rows(np.column_stack((w * target, w)), range(len(target))) + return float(weighted_y / total_w) + + +def squared_error(raw, y, weight=None): + """Weighted mean half-square loss; derivatives remain UNWEIGHTED per row.""" + target = finite_vector(y, "target") + prediction = finite_vector(raw, "raw", len(target)) + w = training_weights(weight, len(target)) + gradient = prediction - target + loss = float(sum_rows((w * gradient * gradient / 2)[:, None], range(len(w)))[0] / sum(w)) + return loss, gradient.copy(), np.ones(len(target), dtype=np.float64) + + +def row_statistics(gradient, curvature, weight=None): + """Multiply raw derivatives by train weight exactly once at this boundary.""" + g = finite_vector(gradient, "gradient") + h = finite_vector(curvature, "curvature", len(g)) + if np.any(h < 0): + raise ValueError("curvature must be nonnegative") + w = training_weights(weight, len(g)) + result = np.column_stack((w * g, w * h)) + if not np.all(np.isfinite(result)): + raise ValueError("non-finite weighted statistics") + return result + + +def nonnegative(value, name): + if not np.isscalar(value) or not np.isfinite(value) or value < 0: + raise ValueError(f"{name} must be finite and nonnegative") + return float(value) + + +def newton_leaf(gradient_sum, curvature_sum, *, reg_lambda=1.0): + regularization = nonnegative(reg_lambda, "reg_lambda") + h = nonnegative(curvature_sum, "curvature_sum") + denominator = h + regularization + if not np.isfinite(gradient_sum) or not np.isfinite(denominator) or denominator <= 0: + raise ValueError("invalid Newton gradient or denominator") + value = -float(gradient_sum) / denominator + if not np.isfinite(value): + raise ValueError("non-finite Newton leaf") + return value + + +def node_score(gradient_sum, curvature_sum, *, reg_lambda=1.0): + """Reduction in the regularized quadratic: G² / (2*(H+lambda)).""" + value = newton_leaf(gradient_sum, curvature_sum, reg_lambda=reg_lambda) + score = -0.5 * float(gradient_sum) * value + if not np.isfinite(score): + raise ValueError("non-finite node score") + return score diff --git a/tests/v1/reference/survival.py b/tests/v1/reference/survival.py new file mode 100644 index 0000000..75ebad8 --- /dev/null +++ b/tests/v1/reference/survival.py @@ -0,0 +1,93 @@ +"""Log-normal event/right-censoring oracle using stdlib tails and a continued fraction.""" + +import math +from statistics import NormalDist + +import numpy as np + +from .positive import _exp, _positive, _result +from .scalar import finite_vector + + +def _scale(sigma): + if not np.isscalar(sigma) or not np.isfinite(sigma) or sigma <= 0: + raise ValueError("sigma must be finite and positive") + sigma = float(sigma) + square = sigma * sigma + if not math.isfinite(square) or square == 0 or not math.isfinite(1 / square): + raise ValueError("sigma geometry exceeds float64") + return sigma + + +def normal_tail(z): + """Return log survival, inverse Mills ratio, and Mills*(Mills-z). + + For z>8, Laplace's continued fraction computes the small correction to z + directly, preserving curvature when subtracting Mills-z would cancel. + This slow scalar oracle is checked against erfc and independent quadrature. + """ + if not np.isscalar(z) or not np.isfinite(z): + raise ValueError("z must be finite") + z = float(z) + log_phi = -z * z / 2 - 0.5 * math.log(2 * math.pi) + if not math.isfinite(log_phi): + raise ValueError("normal tail exceeds float64") + if z > 8: + correction = 0.0 + for n in range(300, 0, -1): + correction = n / (z + correction) + mills = z + correction + return log_phi - math.log(mills), mills, mills * correction + logsf = ( + math.log(math.erfc(z / math.sqrt(2)) / 2) + if z >= 0 + else math.log1p(-math.erfc(-z / math.sqrt(2)) / 2) + ) + mills = math.exp(log_phi - logsf) + return logsf, mills, mills * (mills - z) + + +def aft(raw, lower, upper, *, sigma=1.0, weight=None): + sigma = _scale(sigma) + lo = _positive(lower, "lower") + f = finite_vector(raw, "raw", len(lo)) + hi = np.asarray(upper, dtype=float) + if hi.shape != lo.shape or np.any(np.isnan(hi)) or np.any(~((hi == lo) | np.isposinf(hi))): + raise ValueError("only exact events and right-censored intervals are supported") + loss, g, h = [], [], [] + for location, lower_time, upper_time in zip(f, lo, hi, strict=True): + z = (math.log(lower_time) - location) / sigma + if lower_time == upper_time: + loss.append( + math.log(lower_time) + math.log(sigma) + z * z / 2 + 0.5 * math.log(2 * math.pi) + ) + g.append(-z / sigma) + h.append(1 / sigma**2) + else: + logsf, mills, curvature = normal_tail(z) + loss.append(-logsf) + g.append(-mills / sigma) + h.append(curvature / sigma**2) + return _result(np.array(loss), np.array(g), np.array(h), weight) + + +def aft_predict(raw, *, sigma=1.0, times=(), probabilities=()): + sigma = _scale(sigma) + f = finite_vector(raw, "raw") + times = _positive(times, "times") + probabilities = finite_vector(probabilities, "probabilities") + if np.any(probabilities <= 0) or np.any(probabilities >= 1): + raise ValueError("probabilities must be strictly between zero and one") + survival = np.array( + [ + [math.exp(normal_tail((math.log(t) - location) / sigma)[0]) for t in times] + for location in f + ] + ).reshape(len(f), len(times)) + quantile_z = np.array([NormalDist().inv_cdf(p) for p in probabilities]) + return { + "median": _exp(f, "AFT median"), + "mean": _exp(f + sigma**2 / 2, "AFT mean"), + "survival": survival, + "quantile": _exp(f[:, None] + sigma * quantile_z, "AFT quantile"), + } diff --git a/tests/v1/reference/tree.py b/tests/v1/reference/tree.py new file mode 100644 index 0000000..f4609c2 --- /dev/null +++ b/tests/v1/reference/tree.py @@ -0,0 +1,326 @@ +"""Exhaustive fixed-bin scalar tree reference, intentionally without histograms. + +Rows are enumerated for every candidate. This deliberately differs from the +planned histogram/prefix-sum production algorithm. Only tiny numeric fixtures +are supported; NaN is an explicit missing marker, never a regular bin code. +""" + +from dataclasses import dataclass, replace +from numbers import Integral + +import numpy as np + +from .scalar import ( + newton_leaf, + node_score, + nonnegative, + row_ids, + row_statistics, + squared_error, + sum_rows, + weighted_mean, +) + + +def numeric_bins(values): + x = np.asarray(values, dtype=np.float64) + if x.ndim != 2 or min(x.shape) == 0 or np.any(np.isinf(x)): + raise ValueError("bins must be a nonempty numeric matrix with optional NaN") + observed = x[~np.isnan(x)] + if np.any(observed < 0) or np.any(observed != np.floor(observed)): + raise ValueError("bins must contain nonnegative integer codes or NaN") + return x + + +@dataclass(frozen=True, order=True) +class Condition: + feature: int + threshold: int + missing_left: bool # False sorts first: direction 0=right, 1=left. + + +@dataclass(frozen=True) +class Candidate: + condition: Condition + left: tuple[int, ...] + right: tuple[int, ...] + gain: float + + +def _conditions(x): + # Include the last observed bin: it can separate observed rows from missing. + return tuple( + Condition(f, int(code), missing_left) + for f in range(x.shape[1]) + for code in sorted(set(x[~np.isnan(x[:, f]), f])) + for missing_left in (False, True) + ) + + +def _route(x, rows, condition): + left, right = [], [] + for row in rows: + value = x[row, condition.feature] + go_left = condition.missing_left if np.isnan(value) else value <= condition.threshold + (left if go_left else right).append(row) + return tuple(left), tuple(right) + + +def enumerate_splits( + bins, + gradient, + curvature, + *, + weight=None, + rows=None, + reg_lambda=1.0, + split_penalty=0.0, + min_child_h=0.0, + information=None, + min_information=0.0, +): + """Return every feasible candidate, including those with nonpositive gain. + + Child H must be positive and meet min_child_h. Independent information + fields are summed without training weights. Gain includes one split penalty. + Keeping nonpositive candidates is essential for symmetric level decisions. + """ + x = numeric_bins(bins) + fields = row_statistics(gradient, curvature, weight) + if len(fields) != len(x): + raise ValueError("derivatives and bins must have the same rows") + selected = row_ids(range(len(x)) if rows is None else rows, len(x)) + regularization = nonnegative(reg_lambda, "reg_lambda") + penalty = nonnegative(split_penalty, "split_penalty") + min_h = nonnegative(min_child_h, "min_child_h") + min_info = nonnegative(min_information, "min_information") + info = None if information is None else np.asarray(information, dtype=np.float64) + if info is not None and ( + info.ndim != 2 + or info.shape[0] != len(x) + or info.shape[1] == 0 + or not np.all(np.isfinite(info)) + or np.any(info < 0) + ): + raise ValueError("information must be a finite nonnegative [N,C] matrix") + if info is None and min_info > 0: + raise ValueError("min_information requires information fields") + if not selected: + return () + parent_g, parent_h = sum_rows(fields, selected) + parent_score = node_score(parent_g, parent_h, reg_lambda=regularization) + result = [] + for condition in _conditions(x): + left, right = _route(x, selected, condition) + if not left or not right: + continue + gl, hl = sum_rows(fields, left) + gr, hr = sum_rows(fields, right) + if hl <= 0 or hr <= 0 or min(hl, hr) < min_h: + continue + if info is not None and ( + np.any(sum_rows(info, left) < min_info) or np.any(sum_rows(info, right) < min_info) + ): + continue + gain = ( + node_score(gl, hl, reg_lambda=regularization) + + node_score(gr, hr, reg_lambda=regularization) + - parent_score + - penalty + ) + if not np.isfinite(gain): + raise ValueError("non-finite split gain") + result.append(Candidate(condition, left, right, float(gain))) + return tuple(result) + + +def best_split(candidates): + eligible = [candidate for candidate in candidates if candidate.gain > 0] + return min(eligible, key=lambda c: (-c.gain, c.condition)) if eligible else None + + +@dataclass(frozen=True) +class Node: + rows: tuple[int, ...] + depth: int + value: float + condition: Condition | None = None + left: int = -1 + right: int = -1 + + +@dataclass(frozen=True) +class Tree: + nodes: tuple[Node, ...] + n_features: int + + def predict(self, bins): + x = numeric_bins(bins) + if x.shape[1] != self.n_features: + raise ValueError("bins feature count differs from the tree") + result = [] + for row in range(len(x)): + node = self.nodes[0] + while node.condition is not None: + left, _ = _route(x, (row,), node.condition) + node = self.nodes[node.left if left else node.right] + result.append(node.value) + return np.array(result, dtype=np.float64) + + +def fit_tree( + bins, + gradient, + curvature, + *, + weight=None, + reg_lambda=1.0, + split_penalty=0.0, + min_child_h=0.0, + information=None, + min_information=0.0, + policy="depthwise", + max_depth=2, + max_leaves=None, +): + """Brute-force growth; best-first rescans leaves rather than using a heap.""" + x = numeric_bins(bins) + if policy not in {"depthwise", "best_first", "symmetric"}: + raise ValueError("unknown growth policy") + if not isinstance(max_depth, Integral) or isinstance(max_depth, bool) or max_depth < 0: + raise ValueError("max_depth must be a nonnegative integer") + if max_leaves is None: + max_leaves = len(x) + if not isinstance(max_leaves, Integral) or isinstance(max_leaves, bool) or max_leaves < 1: + raise ValueError("max_leaves must be a positive integer") + kwargs = dict( + weight=weight, + reg_lambda=reg_lambda, + split_penalty=split_penalty, + min_child_h=min_child_h, + information=information, + min_information=min_information, + ) + # Validate even when the caller requests a root-only tree. + root_candidates = enumerate_splits(x, gradient, curvature, **kwargs) + fields = row_statistics(gradient, curvature, weight) + + def leaf(rows, depth): + g, h = sum_rows(fields, rows) + return Node(rows, depth, newton_leaf(g, h, reg_lambda=reg_lambda)) + + nodes = [leaf(tuple(range(len(x))), 0)] + + def candidates(node_id): + if node_id == 0: + return root_candidates + return enumerate_splits(x, gradient, curvature, rows=nodes[node_id].rows, **kwargs) + + def split(node_id, candidate): + node = nodes[node_id] + left, right = len(nodes), len(nodes) + 1 + nodes[node_id] = replace(node, condition=candidate.condition, left=left, right=right) + nodes.extend((leaf(candidate.left, node.depth + 1), leaf(candidate.right, node.depth + 1))) + return left, right + + leaves, frontier = {0}, [0] + while frontier and len(leaves) < max_leaves: + if policy == "best_first": + choices = [ + (i, best_split(candidates(i))) for i in sorted(leaves) if nodes[i].depth < max_depth + ] + choices = [(i, candidate) for i, candidate in choices if candidate is not None] + if not choices: + break + chosen = [min(choices, key=lambda item: (-item[1].gain, item[0], item[1].condition))] + elif policy == "symmetric": + if nodes[frontier[0]].depth >= max_depth or 2 * len(leaves) > max_leaves: + break + by_node = {i: {c.condition: c for c in candidates(i)} for i in frontier} + common = set.intersection(*(set(by_node[i]) for i in frontier)) + if not common: + break + totals = { + condition: sum(by_node[i][condition].gain for i in frontier) for condition in common + } + condition = min(common, key=lambda c: (-totals[c], c)) + if totals[condition] <= 0: + break + chosen = [(i, by_node[i][condition]) for i in frontier] + else: + choices = [ + (i, best_split(candidates(i))) for i in frontier if nodes[i].depth < max_depth + ] + choices = [(i, candidate) for i, candidate in choices if candidate is not None] + chosen = sorted(choices, key=lambda item: (-item[1].gain, item[0], item[1].condition))[ + : max_leaves - len(leaves) + ] + if not chosen: + break + next_frontier = [] + # Stable node IDs are independent of the ranking used to select a layer. + for node_id, candidate in sorted(chosen): + children = split(node_id, candidate) + leaves.remove(node_id) + leaves.update(children) + next_frontier.extend(children) + frontier = next_frontier + return Tree(tuple(nodes), x.shape[1]) + + +@dataclass(frozen=True) +class Step: + gradient: tuple[float, ...] + raw_before: tuple[float, ...] + raw_after: tuple[float, ...] + loss_before: float + loss_after: float + tree: Tree + coefficient: float + + +@dataclass(frozen=True) +class BoostTrace: + base: float + n_features: int + steps: tuple[Step, ...] + + def predict(self, bins): + x = numeric_bins(bins) + if x.shape[1] != self.n_features: + raise ValueError("bins feature count differs from the model") + raw = np.full(len(x), self.base, dtype=np.float64) + for step in self.steps: + raw += step.coefficient * step.tree.predict(x) + return raw + + +def boost_squared(bins, y, *, weight=None, rounds=2, learning_rate=0.1, **tree_options): + """A tiny fixed-step trace, not a trainer API or persistence implementation.""" + x = numeric_bins(bins) + if not isinstance(rounds, Integral) or isinstance(rounds, bool) or rounds < 0: + raise ValueError("rounds must be a nonnegative integer") + coefficient = nonnegative(learning_rate, "learning_rate") + base = weighted_mean(y, weight) + raw = np.full(len(x), base, dtype=np.float64) + # Validate aligned labels even if rounds=0. + squared_error(raw, y, weight) + steps = [] + for _ in range(rounds): + loss_before, gradient, curvature = squared_error(raw, y, weight) + tree = fit_tree(x, gradient, curvature, weight=weight, **tree_options) + updated = raw + coefficient * tree.predict(x) + loss_after, _, _ = squared_error(updated, y, weight) + steps.append( + Step( + tuple(gradient), + tuple(raw), + tuple(updated), + loss_before, + loss_after, + tree, + coefficient, + ) + ) + raw = updated + return BoostTrace(base, x.shape[1], tuple(steps)) diff --git a/tests/v1/reference/vector.py b/tests/v1/reference/vector.py new file mode 100644 index 0000000..e5dc267 --- /dev/null +++ b/tests/v1/reference/vector.py @@ -0,0 +1,107 @@ +"""Small shared-stump oracle with separate split projection and complete vector leaves.""" + +from dataclasses import dataclass + +import numpy as np + +from .scalar import newton_leaf, training_weights +from .tree import Condition, _route, enumerate_splits, numeric_bins + + +def _matrix(values, name): + result = np.asarray(values, dtype=np.float64) + if result.ndim != 2 or min(result.shape) == 0 or not np.all(np.isfinite(result)): + raise ValueError(f"{name} must be a nonempty finite matrix") + return result + + +@dataclass(frozen=True) +class TargetScale: + mean: tuple[float, ...] + scale: tuple[float, ...] + constant: tuple[bool, ...] + + @classmethod + def fit(cls, target): + y = _matrix(target, "target") + mean, scale = np.mean(y, axis=0), np.std(y, axis=0) + if not np.all(np.isfinite(mean)) or not np.all(np.isfinite(scale)): + raise ValueError("target moments exceed float64") + constant = scale == 0 + return cls( + tuple(mean), tuple(np.where(constant, 1.0, scale)), tuple(bool(c) for c in constant) + ) + + def _validate(self, values): + values = _matrix(values, "values") + if values.shape[1] != len(self.mean): + raise ValueError("target output schema mismatch") + return values + + def transform(self, values): + return (self._validate(values) - self.mean) / self.scale + + def inverse(self, values): + return self._validate(values) * self.scale + self.mean + + +@dataclass(frozen=True) +class VectorStump: + n_features: int + condition: Condition | None + left: tuple[float, ...] + right: tuple[float, ...] + + def predict(self, bins): + x = numeric_bins(bins) + if x.shape[1] != self.n_features: + raise ValueError("feature schema mismatch") + if self.condition is None: + return np.tile(self.left, (len(x), 1)) + left, _ = _route(x, tuple(range(len(x))), self.condition) + prediction = np.tile(self.right, (len(x), 1)) + prediction[list(left)] = self.left + return prediction + + +def fit_vector_stump(bins, raw, target, *, weight=None, projection=None, reg_lambda=1.0): + x, raw, target = numeric_bins(bins), _matrix(raw, "raw"), _matrix(target, "target") + if raw.shape != target.shape or len(raw) != len(x): + raise ValueError("raw, target and bins must align") + w = training_weights(weight, len(x)) + gradient = raw - target + projection = np.eye(raw.shape[1]) if projection is None else _matrix(projection, "projection") + if projection.shape[0] != raw.shape[1] or np.any(np.sum(projection**2, axis=0) == 0): + raise ValueError("projection must have K rows and nonzero columns") + # Squared-target sketch: projected residual has unit curvature scaled by + # column norm squared. This only ranks splits; it never changes leaf outputs. + split_g = gradient @ projection + split_h = np.broadcast_to(np.sum(projection**2, axis=0), split_g.shape) + candidates = [ + { + c.condition: c + for c in enumerate_splits( + x, split_g[:, k], split_h[:, k], weight=w, reg_lambda=reg_lambda + ) + } + for k in range(split_g.shape[1]) + ] + common = set.intersection(*(set(c) for c in candidates)) + gains = {c: sum(channel[c].gain for channel in candidates) for c in common} + condition = min(common, key=lambda c: (-gains[c], c)) if common else None + if condition is not None and gains[condition] <= 0: + condition = None + rows = tuple(range(len(x))) + left, right = (rows, rows) if condition is None else _route(x, rows, condition) + + def leaf(selected): + return tuple( + newton_leaf( + sum(w[i] * gradient[i, k] for i in selected), + sum(w[i] for i in selected), + reg_lambda=reg_lambda, + ) + for k in range(raw.shape[1]) + ) + + return VectorStump(x.shape[1], condition, leaf(left), leaf(right)) diff --git a/tests/v1/test_adult_data.py b/tests/v1/test_adult_data.py new file mode 100644 index 0000000..fa41ff5 --- /dev/null +++ b/tests/v1/test_adult_data.py @@ -0,0 +1,85 @@ +"""Official source separation, categorical missingness and stratified Adult splits.""" + +import numpy as np +import pytest +from benchmarks.v1.adult import parse_source, stratified_split + +ROW = "39, Private, 77516, Bachelors, 13, Never-married, ?, Not-in-family, White, Male, 0, 0, 40, United-States, <=50K" + + +def test_missing_and_excluded_weight_and_test_label(): + train = parse_source((ROW + "\n").encode(), "adult.data") + test = parse_source(("|1x3 Cross validator\n" + ROW + ".\n").encode(), "adult.test") + assert len(train["x"][0]) == 13 + assert train["x"][0][5] is None + assert train["x"] == test["x"] and train["y"] == test["y"] == [0] + assert train["row_ids"] != test["row_ids"] + altered = parse_source((ROW.replace("77516", "99999") + "\n").encode(), "adult.data") + assert altered["x"] == train["x"] + + +@pytest.mark.parametrize("label", ["<=50K.", ">50K..", "unknown"]) +def test_invalid_training_labels(label): + with pytest.raises(ValueError): + parse_source(ROW.replace("<=50K", label).encode(), "adult.data") + + +@pytest.mark.parametrize("line", [ROW, ROW + "..", ROW.replace("39,", "NaN,") + "."]) +def test_invalid_test_rows(line): + with pytest.raises(ValueError): + parse_source(line.encode(), "adult.test") + + +def test_stratification_is_complete_seeded_and_isolated(): + labels = np.array([0] * 11 + [1] * 9) + before = np.random.get_state() + for seed in range(5): + train, val = stratified_split(labels, seed) + assert np.bincount(labels[train]).tolist() == [8, 7] + assert np.bincount(labels[val]).tolist() == [3, 2] + np.testing.assert_array_equal(np.sort(np.concatenate([train, val])), np.arange(20)) + for a, b in zip((train, val), stratified_split(labels, seed), strict=True): + np.testing.assert_array_equal(a, b) + after = np.random.get_state() + np.testing.assert_array_equal(before[1], after[1]) + assert before[2:] == after[2:] + + +@pytest.mark.parametrize( + "labels,seed", + [ + ([0, 0], 0), + ([0, 1], 0), + ([False, True], 0), + ([0, 1, 2], 0), + ([0, 0, 1, 1], -1), + ([0, 0, 1, 1], True), + ], +) +def test_invalid_split(labels, seed): + with pytest.raises(ValueError): + stratified_split(labels, seed) + + +def test_test_categories_do_not_change_training_records(): + train = parse_source(ROW.encode(), "adult.data") + test = parse_source((ROW.replace("Private", "Test-only") + ".").encode(), "adult.test") + assert test["x"][0][1] == "Test-only" and train["x"][0][1] == "Private" + + +def test_archive_and_frozen_source(tmp_path): + import json + from pathlib import Path + + from benchmarks.v1 import adult + + path = tmp_path / "bad.zip" + path.write_bytes(b"bad") + with pytest.raises(ValueError, match="hash"): + adult.load_archive(path) + module = Path(adult.__file__) + frozen = json.loads((module.parent / "datasets/adult.json").read_text()) + assert frozen["provenance"]["adapter_sha256"] == adult.digest(module.read_bytes()) + assert frozen["source_records"]["adult.data"]["rows"] == 32561 + assert frozen["source_records"]["adult.test"]["rows"] == 16281 + assert len({f["test"]["row_ids_sha256"] for f in frozen["folds"]}) == 1 diff --git a/tests/v1/test_artifact_judge.py b/tests/v1/test_artifact_judge.py new file mode 100644 index 0000000..e919293 --- /dev/null +++ b/tests/v1/test_artifact_judge.py @@ -0,0 +1,263 @@ +"""Adversarial checks for evidence integrity, not synthetic quality claims.""" + +import hashlib +import json + +import pytest +from benchmarks.v1.judge import cache_key, judge + + +def digest(data): + return hashlib.sha256(data).hexdigest() + + +@pytest.fixture +def bundle(tmp_path): + manifest = { + "schema": "openboost-integrity-v0", + "protocol_sha256": "a" * 64, + "provenance": {"code_sha": "b" * 40, "dirty": False, "environment": {"os": "fixture"}}, + "expected": [], + } + cases = [] + for i in range(1, 14): + cell = { + "id": f"A{i}/cpu/0", + "application": f"A{i}", + "required": True, + "backend": "cpu", + "dataset_sha256": "c" * 64, + "split_sha256": "d" * 64, + "preprocessing_sha256": "e" * 64, + "config": {"depth": 2}, + "seed": 0, + "model": "fixture", + "fold": "0", + } + manifest["expected"].append(cell) + # Compute keys only after the complete matrix is frozen. + for cell in manifest["expected"]: + artifacts = {} + for role, data in { + "predictions": b"[1.0, 2.0]", + "model": b"fixture model", + "log": b"ok", + }.items(): + name = cell["application"] + "-" + role + ".json" + (tmp_path / name).write_bytes(data) + artifacts[role] = {"path": name, "sha256": digest(data)} + cases.append( + { + "id": cell["id"], + "status": "pass", + "cache_key": cache_key(manifest, cell), + "backend": "cpu", + "fallback": False, + "exit_code": 0, + "artifacts": artifacts, + "metrics": {"loss": 0.2}, + "reason": "", + } + ) + return tmp_path, manifest, cases + + +def test_missing_required_fold_fails(bundle): + root, manifest, cases = bundle + result = judge(manifest, cases[:-1], root) + assert not result["integrity_pass"] + assert any("missing case" in e for e in result["errors"]) + + +def test_valid_bundle_is_only_integrity_not_quality(bundle): + root, manifest, cases = bundle + result = judge(manifest, cases, root) + assert result["integrity_pass"] + assert result["gate_results"] == {} + assert len(result["statuses"]) == 13 + + +@pytest.mark.parametrize("status", ["not_run", "fail", "unsupported", "error", "timeout"]) +def test_every_required_nonpass_fails(bundle, status): + root, manifest, cases = bundle + cases[0].update(status=status, reason="intentional failure", exit_code=None) + assert not judge(manifest, cases, root)["integrity_pass"] + + +@pytest.mark.parametrize( + "field,value", + [ + ("backend", "cuda"), + ("fallback", True), + ("exit_code", 2), + ("exit_code", None), + ("exit_code", False), + ("metrics", {"loss": float("nan")}), + ("metrics", {"loss": float("inf")}), + ("metrics", {}), + ("status", "skipped"), + ], +) +def test_false_pass_claims_fail(bundle, field, value): + root, manifest, cases = bundle + cases[0][field] = value + assert not judge(manifest, cases, root)["integrity_pass"] + + +@pytest.mark.parametrize( + "mutation", + ["config", "split", "dataset", "preprocessing", "protocol", "code", "environment", "seed"], +) +def test_cache_binds_every_input(bundle, mutation): + root, manifest, cases = bundle + cell = manifest["expected"][0] + if mutation in {"split", "dataset", "preprocessing"}: + cell[mutation + "_sha256"] = "f" * 64 + elif mutation == "config": + cell["config"]["depth"] = 3 + elif mutation == "protocol": + manifest["protocol_sha256"] = "f" * 64 + elif mutation == "code": + manifest["provenance"]["code_sha"] = "f" * 40 + elif mutation == "environment": + manifest["provenance"]["environment"]["threads"] = 8 + else: + cell["seed"] = 1 + report = judge(manifest, cases, root) + assert not report["integrity_pass"] + assert any("cache identity" in e for e in report["errors"]) + + +@pytest.mark.parametrize( + "mutation", ["duplicate", "unknown", "missing_predictions", "missing_log", "corrupt", "deleted"] +) +def test_missing_or_conflicting_evidence_fails(bundle, mutation): + root, manifest, cases = bundle + if mutation == "duplicate": + cases.append(cases[0]) + elif mutation == "unknown": + cases[0]["id"] = "unlisted" + elif mutation.startswith("missing_"): + del cases[0]["artifacts"][mutation.removeprefix("missing_")] + else: + path = root / cases[0]["artifacts"]["predictions"]["path"] + if mutation == "deleted": + path.unlink() + else: + path.write_text("[42]") + assert not judge(manifest, cases, root)["integrity_pass"] + + +@pytest.mark.parametrize( + "data", [b"[NaN]", b"[1e999]", b"[]", b"[true]", b"[[1],[1,2]]", b'"not predictions"'] +) +def test_rehashed_invalid_predictions_still_fail(bundle, data): + root, manifest, cases = bundle + entry = cases[0]["artifacts"]["predictions"] + (root / entry["path"]).write_bytes(data) + entry["sha256"] = digest(data) + assert not judge(manifest, cases, root)["integrity_pass"] + + +@pytest.mark.parametrize("path", ["../outside", "/tmp/outside"]) +def test_path_escape_is_rejected(bundle, path): + root, manifest, cases = bundle + cases[0]["artifacts"]["predictions"]["path"] = path + report = judge(manifest, cases, root) + assert any("unsafe artifact path" in e for e in report["errors"]) + + +def test_symlink_escape_is_rejected(bundle, tmp_path_factory): + root, manifest, cases = bundle + outside = tmp_path_factory.mktemp("outside") / "prediction.json" + outside.write_bytes(b"[1.0, 2.0]") + link = root / "escape.json" + link.symlink_to(outside) + cases[0]["artifacts"]["predictions"]["path"] = link.name + assert any("escapes run directory" in e for e in judge(manifest, cases, root)["errors"]) + + +@pytest.mark.parametrize( + "mutation", ["empty", "duplicate", "optional_only", "dirty", "nan_config", "schema"] +) +def test_invalid_manifest_fails(bundle, mutation): + root, manifest, cases = bundle + if mutation == "empty": + manifest["expected"] = [] + elif mutation == "duplicate": + manifest["expected"].append(manifest["expected"][0]) + elif mutation == "optional_only": + manifest["expected"][0]["required"] = False + elif mutation == "dirty": + manifest["provenance"]["dirty"] = True + elif mutation == "nan_config": + manifest["expected"][0]["config"]["rate"] = float("nan") + else: + manifest["schema"] = "future" + assert not judge(manifest, cases, root)["integrity_pass"] + + +def test_optional_gpu_unsupported_is_visible_and_missing_is_failure(bundle): + from copy import deepcopy + + root, manifest, cases = bundle + gpu = deepcopy(manifest["expected"][0]) + gpu.update(id="A1/cuda/0", backend="cuda", required=False) + manifest["expected"].append(gpu) + for cell, record in zip(manifest["expected"], cases, strict=False): + record["cache_key"] = cache_key(manifest, cell) + record = deepcopy(cases[0]) + record.update( + id=gpu["id"], + status="unsupported", + backend="none", + exit_code=None, + cache_key=cache_key(manifest, gpu), + reason="no CUDA device", + metrics={}, + ) + record["artifacts"] = {"log": record["artifacts"]["log"]} + assert not judge(manifest, cases, root)["integrity_pass"] + cases.append(record) + report = judge(manifest, cases, root) + assert report["integrity_pass"] and report["statuses"][gpu["id"]] == "unsupported" + assert report["gate_results"] == {} + gpu["required"] = True + for cell, record in zip(manifest["expected"], cases, strict=True): + record["cache_key"] = cache_key(manifest, cell) + assert not judge(manifest, cases, root)["integrity_pass"] + + +def test_cli_exit_and_strict_json(bundle): + import subprocess + import sys + + root, manifest, cases = bundle + (root / "manifest.json").write_text(json.dumps(manifest)) + path = root / "cases.jsonl" + path.write_text("\n".join(json.dumps(c) for c in cases) + "\n") + command = [sys.executable, "-m", "benchmarks.v1.judge", str(root)] + success = subprocess.run(command, capture_output=True, text=True) + assert success.returncode == 0 + assert json.loads(success.stdout)["gate_results"] == {} + path.write_text("\n".join(json.dumps(c) for c in cases[:-1])) + failure = subprocess.run(command, capture_output=True, text=True) + assert failure.returncode == 1 + assert not json.loads(failure.stdout)["integrity_pass"] + path.write_text('{"id": "first", "id": "second"}\n') + duplicate = subprocess.run(command, capture_output=True, text=True) + assert duplicate.returncode == 1 + assert "duplicate JSON field" in duplicate.stdout + + +def test_missing_second_fold_fails_even_with_all_applications_present(bundle): + from copy import deepcopy + + root, manifest, cases = bundle + second_fold = deepcopy(manifest["expected"][0]) + second_fold.update(id="A1/cpu/1", fold="1", seed=1, split_sha256="f" * 64) + manifest["expected"].append(second_fold) + for cell, record in zip(manifest["expected"], cases, strict=False): + record["cache_key"] = cache_key(manifest, cell) + report = judge(manifest, cases, root) + assert report["errors"] == ["missing case: A1/cpu/1"] diff --git a/tests/v1/test_author_reference.py b/tests/v1/test_author_reference.py new file mode 100644 index 0000000..10134cc --- /dev/null +++ b/tests/v1/test_author_reference.py @@ -0,0 +1,193 @@ +"""D1/D3/D4 mathematical and mutation counterexamples.""" + +import numpy as np +import pytest + +from .reference.author import ( + expectile, + expectile_base, + ordered_normal, + penalized_quantile, + penalized_tree, +) +from .reference.coupled import normal +from .reference.scalar import squared_error +from .reference.tree import fit_tree + + +def test_expectile_hand_signs_weights_and_symmetric_limit(): + loss, g, h = expectile([0, 2, 1], [1, 1, 1], tau=0.8, weight=[1, 3, 0]) + assert loss == pytest.approx(0.35) + np.testing.assert_allclose(g, [-1.6, 0.4, 0]) + np.testing.assert_allclose(h, [1.6, 0.4, 1.6]) # declared r=0 branch, no smooth-Hessian claim + assert expectile_base([0, 2], tau=0.8) == pytest.approx(1.6) + assert expectile_base([0, 2], tau=0.8, weight=[3, 1]) == pytest.approx(8 / 7) + for actual, expected in zip( + expectile([0.3, 2], [1, 1], tau=0.5), squared_error([0.3, 2], [1, 1]), strict=True + ): + np.testing.assert_allclose(actual, expected) + + +def test_expectile_calculus_base_and_two_rounds(): + raw = np.array([-0.3, 2.2]) + y = np.array([0.0, 2.0]) + eps = 1e-5 + _, g, h = expectile(raw, y, tau=0.8) + for i in range(2): + delta = np.eye(2)[i] * eps + plus, minus = expectile(raw + delta, y, tau=0.8), expectile(raw - delta, y, tau=0.8) + assert (plus[0] - minus[0]) / eps == pytest.approx(g[i], abs=1e-9) + assert (plus[1][i] - minus[1][i]) / (2 * eps) == pytest.approx(h[i], abs=1e-9) + base = expectile_base(y, tau=0.8) + assert sum(expectile([base, base], y, tau=0.8)[1]) == pytest.approx(0, abs=1e-14) + raw = np.full(2, base) + predictions = raw.copy() + for iteration in range(2): + _, g, h = expectile(raw, y, tau=0.8) + tree = fit_tree([[0], [1]], g, h, max_depth=1) + expected = ( + [-16 / 35, 16 / 65] + if iteration == 0 + else [-(0.4 * (1.6 - 8 / 175)) / 1.4, 1.6 * (0.4 - 8 / 325) / 2.6] + ) + np.testing.assert_allclose(tree.predict([[0], [1]]), expected) + raw += 0.1 * tree.predict([[0], [1]]) + predictions += 0.1 * np.array(expected) + np.testing.assert_allclose(raw, predictions) + + +def objective(value, residual, weight, q, penalty, anchor): + r = np.asarray(residual) - value + return float( + np.dot(weight, np.maximum(q * r, (q - 1) * r)) + penalty * (value - anchor) ** 2 / 2 + ) + + +def test_penalized_quantile_stationary_point_is_not_default_quantile(): + value = penalized_quantile([0, 2, 10], 0.5, [1, 3, 1], penalty=1, anchor=0) + assert value == 1.5 # lies strictly between residual breakpoints + assert penalized_quantile([0, 2, 10], 0.5, [1, 3, 1], penalty=10, anchor=0) == 0.15 + assert penalized_quantile([0, 2, 10], 0.5, [1, 3, 1], penalty=0.1, anchor=0) == 2 + + +@pytest.mark.parametrize("q", [0.1, 0.5, 0.9]) +@pytest.mark.parametrize("anchor", [-3.0, 1.0, 8.0]) +def test_penalized_quantile_subgradient_and_global_minimum(q, anchor): + residual = np.array([0.0, 2.0, 2.0, 10.0]) + weight = np.array([1.0, 0.0, 3.0, 1.0]) + penalty = 0.7 + value = penalized_quantile(residual, q, weight, penalty=penalty, anchor=anchor) + left = sum(weight[residual < value]) - q * sum(weight) + penalty * (value - anchor) + right = sum(weight[residual <= value]) - q * sum(weight) + penalty * (value - anchor) + assert left <= 1e-12 and right >= -1e-12 + best = objective(value, residual, weight, q, penalty, anchor) + for candidate in np.linspace(-5, 12, 201): + assert objective(candidate, residual, weight, q, penalty, anchor) >= best - 1e-12 + + +def test_penalized_tree_two_rounds_uses_current_routed_residual(): + y = np.array([0.0, 2.0, 10.0]) + raw = np.full(3, 2.0) + for iteration in range(2): + tree = penalized_tree( + [[0], [0], [1]], raw, y, 0.5, weight=[2, 1, 2], penalty=0.1, anchor=0, max_depth=1 + ) + assert tree.predict([[1]])[0] == pytest.approx(8 if iteration == 0 else 7.2) + assert tree.predict([[0]])[0] == pytest.approx(-2 if iteration == 0 else -1.8) + raw += 0.1 * tree.predict([[0], [0], [1]]) + np.testing.assert_allclose(raw, [1.62, 1.62, 3.52]) + + +def test_d4_exact_rates_reversed_direction_and_nan_candidates(): + raw = np.zeros((2, 2)) + rates = tuple(0.1 * 0.5**j for j in range(6)) + + def reversed_gradient(r, y, weight=None): + loss, g, metric = normal(r, y, weight=weight) + return loss, -g, metric + + rejected = ordered_normal([[0], [0]], raw, [1, 3], objective=reversed_gradient) + for result in rejected: + assert not result.accepted and result.terms == () + assert tuple(t[0] for t in result.trials) == rates + np.testing.assert_array_equal(result.raw_after, raw) + + def invalid_candidate(r, y, weight=None): + loss, g, metric = normal(r, y, weight=weight) + return (loss if np.array_equal(r, raw) else float("nan")), g, metric + + for result in ordered_normal([[0], [0]], raw, [1, 3], objective=invalid_candidate): + assert not result.accepted and len(result.trials) == 6 + assert all(t[1] is None for t in result.trials) + + +def test_d4_two_round_ordered_commit_and_first_rejection(): + raw = np.zeros((2, 2)) + for _ in range(2): + first, second = ordered_normal([[0], [0]], raw, [1, 3]) + assert first.accepted and second.accepted + assert second.raw_before == first.raw_after + assert first.loss_after < first.loss_before and second.loss_after < second.loss_before + raw = np.array(second.raw_after) + + def reject_mean(r, y, weight=None): + loss, g, metric = normal(r, y, weight=weight) + g[:, 0] *= -1 + return loss, g, metric + + first, second = ordered_normal([[0], [0]], np.zeros((2, 2)), [1, 3], objective=reject_mean) + assert not first.accepted and second.accepted + assert second.raw_before == first.raw_before + + +def test_penalty_weight_mass_is_not_silently_normalized(): + r = [0, 2, 10] + original = penalized_quantile(r, 0.5, [1, 3, 1], penalty=1, anchor=0) + replicated = penalized_quantile([0, 2, 2, 2, 10], 0.5, penalty=1, anchor=0) + assert original == replicated == 1.5 + assert penalized_quantile(r, 0.5, [2, 6, 2], penalty=1, anchor=0) == 2 + assert penalized_quantile(r, 0.5, [2, 6, 2], penalty=2, anchor=0) == original + assert penalized_quantile([-100, 0, 2, 10], 0.5, [0, 1, 3, 1], penalty=1, anchor=0) == original + + +@pytest.mark.parametrize("tau", [0, 1, -1, np.nan]) +def test_expectile_rejects_invalid_asymmetry(tau): + with pytest.raises(ValueError): + expectile_base([0, 2], tau=tau) + with pytest.raises(ValueError): + expectile([0, 0], [0, 2], tau=tau) + + +@pytest.mark.parametrize("penalty", [0, -1, np.nan, np.inf]) +def test_penalized_leaf_requires_finite_positive_penalty(penalty): + with pytest.raises(ValueError): + penalized_quantile([0, 2], 0.5, penalty=penalty, anchor=0) + + +def test_expectile_base_replication_and_zero_weight_outlier(): + value = expectile_base([0, 2], tau=0.8, weight=[3, 1]) + assert expectile_base([0, 0, 0, 2], tau=0.8) == pytest.approx(value) + assert expectile_base([-100, 0, 2], tau=0.8, weight=[0, 3, 1]) == pytest.approx(value) + assert expectile_base([2, 2], tau=0.8) == 2 + with pytest.raises(ValueError): + expectile_base([0, 2], weight=[0, 0]) + + +def test_ordered_rejection_does_not_mutate_input_or_global_rng(): + raw = np.zeros((2, 2)) + original = raw.copy() + rng_before = np.random.get_state() + + def reversed_gradient(r, y, weight=None): + loss, g, metric = normal(r, y, weight=weight) + return loss, -g, metric + + records = ordered_normal([[0], [0]], raw, [1, 3], objective=reversed_gradient) + rng_after = np.random.get_state() + assert rng_before[0] == rng_after[0] and rng_before[2:] == rng_after[2:] + np.testing.assert_array_equal(rng_before[1], rng_after[1]) + np.testing.assert_array_equal(raw, original) + raw[:] = 100 + for record in records: + np.testing.assert_array_equal(record.raw_after, original) + assert record.loss_after == record.loss_before and record.terms == () diff --git a/tests/v1/test_auxiliary_metrics.py b/tests/v1/test_auxiliary_metrics.py new file mode 100644 index 0000000..27b525b --- /dev/null +++ b/tests/v1/test_auxiliary_metrics.py @@ -0,0 +1,83 @@ +import numpy as np +import pytest +from benchmarks.v1.auxiliary import ( + classification, + normal_pit, + paired_interval, + structure_errors, + survival, +) +from benchmarks.v1.preprocessing import censoring_support + + +def test_weighted_auc_ties_and_per_class_counts(): + r = classification("A2", [0, 1, 0, 1], [0.1, 0.5, 0.5, 0.9], weight=[1, 2, 3, 4]) + assert r["binary_auc"] == pytest.approx(21 / 24) + assert r["classes"]["1"]["weight"] == 6 + assert r["classes"]["0"]["rows"] == 2 + + +def test_absent_class_is_explicit_not_zero_auc(): + r = classification("A2", [0, 0], [0.1, 0.2]) + assert r["binary_auc"] is None + assert r["classes"]["1"]["recall"] is None + + +def test_normal_pit_mass_is_conserved(): + r = normal_pit([0.0, 0.0], [[0.0, 1.0], [0.0, 2.0]], weight=[1, 3]) + assert sum(r["pit_decile_mass"]) == 1 + assert r["pit_decile_mass"][5] == 1 + + +def test_ipcw_censored_row_does_not_count_as_death(): + support = censoring_support([1, 2, 3, 4], [1, 0, 1, 1]) + support["grid"] = [2.0] + p = np.column_stack([np.full(3, np.log(2)), np.ones(3)]) + r = survival([1, 2, 4], [1, 0, 1], p, support) + assert r["ipcw_brier"] == pytest.approx([(0.25 + 0.25 / (2 / 3)) / 3]) + assert r["contributing_rows"] == [2] + + +def test_event_censor_tie_uses_frozen_right_continuous_G(): + support = censoring_support([1, 1, 2, 3], [1, 0, 1, 1]) + support["grid"] = [1.0] + r = survival([1, 2], [1, 1], [[0.0, 1.0], [0.0, 1.0]], support) + assert r["ipcw_brier"] == pytest.approx([0.375]) + assert r["harrell_c"] == 0.5 + + +def test_grid_outside_censoring_support_is_not_clipped(): + support = censoring_support([1, 2, 3], [1, 1, 0]) + support["grid"] = [3.0] + with pytest.raises(ValueError, match="support"): + survival([1, 2], [1, 1], [[0.0, 1.0], [0.0, 1.0]], support) + + +def test_long_test_followup_needs_no_extrapolated_G_for_earlier_grid(): + support = censoring_support([1, 2, 3], [1, 1, 1]) + support["grid"] = [1.0] + r = survival([1, 100], [1, 1], [[0.0, 1.0], [0.0, 1.0]], support) + assert r["ipcw_brier"] == pytest.approx([0.25]) + + +def test_no_comparable_pairs_and_unsupported_weights_are_explicit(): + support = censoring_support([1, 2, 3], [1, 1, 1]) + support["grid"] = [1.0] + r = survival([2, 3], [0, 0], [[0.0, 1.0], [1.0, 1.0]], support) + assert r["harrell_c"] is None and r["comparable_pairs"] == 0 + with pytest.raises(ValueError, match="unit"): + survival([2, 3], [0, 0], [[0.0, 1.0], [1.0, 1.0]], support, weight=[1, 2]) + + +def test_structural_support_and_empty_strata(): + r = structure_errors([1, 2, 4], [1.0, 2.0, 3.0], [1.0, 1.0, 1.0], 1, 2) + assert r["below"]["rmse"] is None + assert r["above"] == {"rows": 1, "rmse": 2.0} + + +def test_exact_paired_interval_preserves_constant_difference_and_rng(): + np.random.seed(41) + before = np.random.get_state() + r = paired_interval([2, 3, 4, 5, 6], [1, 2, 3, 4, 5]) + assert r["percentile95"] == [1.0, 1.0] + np.testing.assert_array_equal(before[1], np.random.get_state()[1]) diff --git a/tests/v1/test_baseline_worker.py b/tests/v1/test_baseline_worker.py new file mode 100644 index 0000000..e5f87aa --- /dev/null +++ b/tests/v1/test_baseline_worker.py @@ -0,0 +1,113 @@ +"""Reject corrupted evaluation inputs before invoking an external trainer.""" + +import numpy as np +import pytest +from benchmarks.v1.baseline_worker import fit, predict_saved + + +def fixture(): + job = dict( + application="A7", + library="xgboost", + device="cpu", + threads=2, + seed=0, + config=dict(rounds=4, learning_rate=0.1, max_depth=2, reg_lambda=1), + ) + arrays = dict( + x_train=np.ones((3, 2)), + y_train=np.ones(3), + x_validation=np.ones((2, 2)), + validation_row_ids=np.array([3, 4]), + exposure_train=np.ones(3), + exposure_validation=np.ones(2), + ) + return job, arrays + + +@pytest.mark.parametrize( + "field,value,reason", + [ + ("validation_row_ids", np.array([3, 3]), "row IDs"), + ("exposure_train", np.array([1.0, np.nan, 1.0]), "exposure"), + ("exposure_validation", np.array([1.0, np.inf]), "exposure"), + ("weight_train", np.array([0.0, 0.0, 0.0]), "weights"), + ], +) +def test_invalid_array_rejected(field, value, reason): + job, arrays = fixture() + arrays[field] = value + with pytest.raises(ValueError, match=reason): + fit(job, arrays) + + +def test_early_stopping_needs_explicit_validation(): + job, arrays = fixture() + job["early_stopping_rounds"] = 50 + with pytest.raises(ValueError, match="early stopping"): + fit(job, arrays) + + +def test_censor_indicator_must_not_be_coerced_to_truthiness(): + job, arrays = fixture() + job["application"] = "A10" + arrays.pop("exposure_train") + arrays.pop("exposure_validation") + arrays["event_train"] = np.array([1, 2, 0]) + with pytest.raises(ValueError, match="survival"): + fit(job, arrays) + + +def test_missing_exposure_cannot_silently_change_saved_model(): + saved = dict(application="A7", library="xgboost", model=None) + with pytest.raises(ValueError, match="exposure required"): + predict_saved(saved, np.ones((2, 2))) + + +def test_unknown_job_option_is_not_ignored(): + job, arrays = fixture() + job["constraints"] = [1, 0] + with pytest.raises(ValueError, match="unsupported job"): + fit(job, arrays) + + +def test_unlocked_test_data_cannot_enter_validation_worker(): + job, arrays = fixture() + arrays["x_test"] = np.ones((2, 2)) + with pytest.raises(ValueError, match="unsupported input"): + fit(job, arrays) + + +@pytest.mark.parametrize("patience", [0, -1, True, 1.5]) +def test_invalid_early_stopping_patience(patience): + job, arrays = fixture() + job["early_stopping_rounds"] = patience + with pytest.raises(ValueError, match="patience"): + fit(job, arrays) + + +def test_invalid_validation_weight_fails_before_training(): + job, arrays = fixture() + job["early_stopping_rounds"] = 2 + arrays["y_validation"] = np.ones(2) + arrays["weight_validation"] = np.array([0.0, 0.0]) + with pytest.raises(ValueError, match="validation weights"): + fit(job, arrays) + + +def test_validation_targets_rejected_when_not_requested(): + job, arrays = fixture() + arrays["y_validation"] = np.ones(2) + with pytest.raises(ValueError, match="unsupported input"): + fit(job, arrays) + + +@pytest.mark.parametrize("target", [np.ones(3), np.empty((3, 0))]) +def test_a6_requires_output_columns_before_importing_trainer(target): + job, arrays = fixture() + job["application"] = "A6" + arrays.pop("exposure_train") + arrays.pop("exposure_validation") + arrays["y_train"] = target + with pytest.raises(ValueError, match="matrix targets"): + fit(job, arrays) diff --git a/tests/v1/test_bike_data.py b/tests/v1/test_bike_data.py new file mode 100644 index 0000000..2337552 --- /dev/null +++ b/tests/v1/test_bike_data.py @@ -0,0 +1,167 @@ +"""Calendar-only data and full-date rolling splits for A5.""" + +import csv +import io +from datetime import date, timedelta + +import numpy as np +import pytest +from benchmarks.v1.bike import FEATURES, parse_hour, rolling_splits + + +def fixture_csv(): + names = [ + "instant", + "dteday", + "season", + "yr", + "mnth", + "hr", + "holiday", + "weekday", + "workingday", + "weathersit", + "temp", + "atemp", + "hum", + "windspeed", + "casual", + "registered", + "cnt", + ] + stream = io.StringIO() + writer = csv.DictWriter(stream, fieldnames=names) + writer.writeheader() + for i in range(40): + day = date(2011, 1, 1) + timedelta(days=i // 2) + row = dict.fromkeys(names, "0") + row.update( + instant=i + 1, + dteday=day.isoformat(), + season=1, + yr=0, + mnth=day.month, + hr=i % 2, + weekday=(day.weekday() + 1) % 7, + workingday=int(day.weekday() < 5), + cnt=i, + ) + writer.writerow(row) + return stream.getvalue().encode() + + +def test_full_dates_and_calendar_only(): + data = parse_hour(fixture_csv()) + assert data.x.shape == (40, 7) + assert FEATURES == ("season", "yr", "mnth", "hr", "holiday", "weekday", "workingday") + for fold in rolling_splits(data.dates): + parts = [set(data.dates[fold[k]]) for k in ("train", "validation", "test")] + assert not parts[0] & parts[1] and not parts[1] & parts[2] + assert max(parts[0]) < min(parts[1]) < min(parts[2]) + first = rolling_splits(data.dates)[0] + np.testing.assert_array_equal(first["train"], np.arange(20)) + np.testing.assert_array_equal(first["validation"], np.arange(20, 24)) + np.testing.assert_array_equal(first["test"], np.arange(24, 28)) + + +def rewrite(data, field, value, row_index=0): + rows = list(csv.DictReader(io.StringIO(data.decode()))) + rows[row_index][field] = value + stream = io.StringIO() + writer = csv.DictWriter(stream, fieldnames=rows[0]) + writer.writeheader() + writer.writerows(rows) + return stream.getvalue().encode() + + +def test_excluded_observations_cannot_change_features_or_split(): + original = fixture_csv() + modified = original + for field in ("weathersit", "temp", "atemp", "hum", "windspeed", "casual", "registered"): + modified = rewrite(modified, field, "unavailable-at-prediction-time") + a, b = parse_hour(original), parse_hour(modified) + np.testing.assert_array_equal(a.x, b.x) + np.testing.assert_array_equal(a.y, b.y) + for left, right in zip(rolling_splits(a.dates), rolling_splits(b.dates), strict=True): + for key in left: + np.testing.assert_array_equal(left[key], right[key]) + changed_target = parse_hour(rewrite(original, "cnt", "9999")) + np.testing.assert_array_equal(a.x, changed_target.x) + assert a.y[0] != changed_target.y[0] + + +@pytest.mark.parametrize( + "field,value", + [ + ("instant", "2"), + ("instant", "0"), + ("cnt", "-1"), + ("cnt", "NaN"), + ("hr", "24"), + ("hr", "1"), + ("dteday", "2011-02-30"), + ("yr", "1"), + ("weekday", "1"), + ("workingday", "1"), + ("season", "0"), + ("holiday", "2"), + ("mnth", "2"), + ], +) +def test_invalid_calendar_counts_and_ids_fail(field, value): + with pytest.raises(ValueError): + parse_hour(rewrite(fixture_csv(), field, value)) + + +def test_empty_and_wrong_schema_fail(): + with pytest.raises(ValueError, match="schema"): + parse_hour(b"date,count\n2011-01-01,1\n") + with pytest.raises(ValueError, match="empty"): + parse_hour(fixture_csv().splitlines()[0] + b"\n") + + +def test_date_splits_are_not_row_percentage_and_retain_full_days(): + dates = np.array(["2011-01-01"] * 20 + [f"2011-01-{i:02}" for i in range(2, 21)]) + folds = rolling_splits(dates) + # Ten of twenty dates, but 29 of 39 rows. Missing hours are not synthesized. + assert len(folds[0]["train"]) == 29 + assert dates[folds[0]["validation"][0]] == "2011-01-11" + assert dates[folds[4]["test"][-1]] == "2011-01-18" + + +@pytest.mark.parametrize("dates", [[], ["2011-01-01"], ["2011-01-02", "2011-01-01"], ["bad"]]) +def test_invalid_split_dates_fail(dates): + with pytest.raises(ValueError): + rolling_splits(dates) + + +def test_archive_corruption_rejected_before_parsing(tmp_path): + from benchmarks.v1.bike import load_archive + + archive = tmp_path / "wrong.zip" + archive.write_bytes(b"not the pinned archive") + with pytest.raises(ValueError, match="archive hash"): + load_archive(archive) + + +def test_array_hash_binds_dtype_shape_and_order(): + from benchmarks.v1.bike import array_hash + + assert array_hash([1, 2], " 0 # no cancellation to zero + + +@pytest.mark.parametrize("target", [[0, 2], [0, 0.5], [0, np.nan]]) +def test_binary_rejects_invalid_codes(target): + with pytest.raises(ValueError): + binary([0, 0], target) + + +@pytest.mark.parametrize("clip", [0, 0.5, np.nan, 1e-20]) +def test_binary_base_requires_explicit_valid_clipping(clip): + with pytest.raises(ValueError): + binary_base([0, 1], clip=clip) + + +def test_softmax_exact_hessian_and_named_diagonal_bound(): + loss, p, g, exact, bound = softmax([[0, 0, 0]], [1]) + assert loss == pytest.approx(np.log(3)) + np.testing.assert_allclose(p, [[1 / 3] * 3]) + np.testing.assert_allclose(g, [[1 / 3, -2 / 3, 1 / 3]]) + np.testing.assert_allclose(exact[0], np.eye(3) / 3 - np.ones((3, 3)) / 9) + np.testing.assert_allclose(bound, [[4 / 9] * 3]) + assert np.linalg.eigvalsh(np.diag(bound[0]) - exact[0]).min() >= -1e-14 + + +def test_softmax_finite_differences_shift_and_class_permutation(): + raw = np.array([[0.3, -1.2, 2.1]]) + loss, p, g, exact, bound = softmax(raw, [2]) + eps = 1e-5 + for k in range(3): + delta = np.eye(3)[k : k + 1] * eps + plus = softmax(raw + delta, [2]) + minus = softmax(raw - delta, [2]) + assert (plus[0] - minus[0]) / (2 * eps) == pytest.approx(g[0, k], abs=1e-9) + np.testing.assert_allclose((plus[2] - minus[2]) / (2 * eps), exact[:, :, k], atol=1e-10) + perm = [2, 0, 1] + shifted = softmax(raw[:, perm] + 1000, [0]) + assert shifted[0] == pytest.approx(loss) + np.testing.assert_allclose(shifted[1], p[:, perm]) + np.testing.assert_allclose(shifted[2], g[:, perm]) + np.testing.assert_allclose(shifted[4], bound[:, perm]) + assert np.sum(p) == pytest.approx(1) + assert np.sum(g) == pytest.approx(0, abs=1e-15) + + +def test_softmax_extremes_weights_and_invalid_labels(): + raw = [[1000, -1000, 0], [0, 0, 0]] + loss, _, g, exact, bound = softmax(raw, [1, 0], weight=[1, 3]) + assert loss == pytest.approx((2000 + 3 * np.log(3)) / 4) + unweighted = softmax(raw, [1, 0]) + for actual, expected in zip((g, exact, bound), unweighted[2:], strict=True): + np.testing.assert_array_equal(actual, expected) + for labels in ([3, 0], [-1, 0], [0.5, 0], [np.nan, 0]): + with pytest.raises(ValueError): + softmax(raw, labels) + + +def test_two_binary_rounds_recompute_derivatives_and_validation_predictions(): + bins = [[0], [0], [1], [1]] + target = [0, 0, 1, 1] + raw = np.zeros(4) + prediction = np.zeros(2) + for step in range(2): + _, g, h = binary(raw, target) + tree = fit_tree(bins, g, h, max_depth=1) + if step == 0: + np.testing.assert_allclose(tree.predict([[0], [1]]), [-2 / 3, 2 / 3]) + else: + p = 1 / (1 + np.exp(1 / 15)) + value = 2 * p / (1 + 2 * p * (1 - p)) + np.testing.assert_allclose(tree.predict([[0], [1]]), [-value, value]) + raw += 0.1 * tree.predict(bins) + prediction += 0.1 * tree.predict([[0], [1]]) + np.testing.assert_allclose(raw, prediction[[0, 0, 1, 1]]) + assert binary(raw, target)[0] < np.log(2) + + +def test_two_softmax_rounds_use_joint_snapshot_and_permutation_equivariance(): + bins = [[0], [1], [2]] + raw = np.zeros((3, 3)) + perm = [2, 0, 1] + reordered = raw[:, perm].copy() + previous = None + for _ in range(2): + _, _, g, _, bound = softmax(raw, [0, 1, 2]) + _, _, gp, _, hp = softmax(reordered, [1, 2, 0]) + if previous is not None: + assert not np.allclose(g, previous) + updates = np.column_stack( + [fit_tree(bins, g[:, k], bound[:, k]).predict(bins) for k in range(3)] + ) + updates_p = np.column_stack( + [fit_tree(bins, gp[:, k], hp[:, k]).predict(bins) for k in range(3)] + ) + raw = raw + 0.1 * updates + reordered = reordered + 0.1 * updates_p + np.testing.assert_allclose(reordered, raw[:, perm], atol=1e-15) + previous = g.copy() + assert softmax(raw, [0, 1, 2])[0] < np.log(3) + + +def test_binary_derivatives_by_finite_difference_and_weight_replication(): + raw = np.array([-0.7, 1.3]) + y = [0, 1] + loss, g, h = binary(raw, y, weight=[1, 3]) + repeated = binary(raw[[0, 1, 1, 1]], [0, 1, 1, 1]) + assert repeated[0] == pytest.approx(loss) + eps = 1e-5 + for i in range(2): + plus = binary([raw[i] + eps], [y[i]]) + minus = binary([raw[i] - eps], [y[i]]) + assert (plus[0] - minus[0]) / (2 * eps) == pytest.approx(g[i], abs=1e-10) + assert (plus[1][0] - minus[1][0]) / (2 * eps) == pytest.approx(h[i], abs=1e-10) + + +def test_softmax_two_round_root_updates_have_independent_hand_solution(): + # All channels use one joint snapshot. Unequal class masses make a root + # update nonzero and expose both weighting and stale second-round geometry. + bins = [[0], [0], [0]] + target = [0, 1, 2] + weight = np.array([1.0, 2.0, 3.0]) + raw = np.zeros((3, 3)) + validation = np.zeros((1, 3)) + for step in range(2): + _, _, g, _, bound = softmax(raw, target, weight=weight) + update = np.array( + [ + fit_tree(bins, g[:, k], bound[:, k], weight=weight, max_depth=0).predict([[0]])[0] + for k in range(3) + ] + ) + if step == 0: + expected = np.array([-3 / 11, 0, 3 / 11]) + else: + exp = np.exp(np.array([-3 / 110, 0, 3 / 110])) + p = exp / sum(exp) + expected = (weight - 6 * p) / (1 + 12 * p * (1 - p)) + np.testing.assert_allclose(update, expected, atol=1e-15) + raw += 0.1 * update + validation += 0.1 * update + np.testing.assert_allclose(raw, np.repeat(validation, 3, axis=0)) + + +@pytest.mark.parametrize( + "raw,target", + [([], []), ([[0]], [0]), ([[0, np.inf]], [0]), ([[0, 0]], []), ([[1e308, -1e308]], [0])], +) +def test_softmax_invalid_shapes_and_unrepresentable_range(raw, target): + with pytest.raises(ValueError): + softmax(raw, target) + + +def test_single_class_base_rejected_and_zero_weight_rows_excluded_from_loss(): + with pytest.raises(ValueError): + binary_base([1, 1], clip=1e-6) + assert binary([0, -1000], [1, 1], weight=[1, 0])[0] == pytest.approx(np.log(2)) + assert softmax([[0, 0, 0], [-1000, 0, 1000]], [0, 0], weight=[1, 0])[0] == pytest.approx( + np.log(3) + ) diff --git a/tests/v1/test_coupled_reference.py b/tests/v1/test_coupled_reference.py new file mode 100644 index 0000000..ca4496e --- /dev/null +++ b/tests/v1/test_coupled_reference.py @@ -0,0 +1,255 @@ +"""A11/A12 geometry and accepted-state checks independent of production.""" + +import math + +import numpy as np +import pytest + +from .reference.coupled import ( + directions, + formula, + formula_base, + formula_predict, + normal, + normal_base, + normal_scores, + step, +) + + +def test_normal_hand_fisher_and_natural_direction(): + raw = [[1, math.log(2)]] + loss, gradient, fisher = normal(raw, [3]) + assert loss == pytest.approx(math.log(2) + 0.5 + 0.5 * math.log(2 * math.pi)) + np.testing.assert_allclose(gradient, [[-0.5, 0]]) + np.testing.assert_allclose(fisher, [[[0.25, 0], [0, 2]]]) + np.testing.assert_allclose(directions(gradient, fisher, mode="full"), [[2, 0]]) + np.testing.assert_allclose(directions(gradient, fisher, mode="ordinary"), [[0.5, 0]]) + base = normal_base([1, 3], weight=[1, 3], minimum_scale=0.1) + np.testing.assert_allclose(base, [2.5, 0.5 * math.log(0.75)]) + np.testing.assert_allclose(normal_base([3, 3], minimum_scale=0.1), [3, math.log(0.1)]) + + +@pytest.mark.parametrize("kind", ["normal", "formula"]) +def test_geometry_gradients_and_formula_jacobian_by_finite_difference(kind): + raw = np.array([[0.2, -0.3], [1.0, 0.4]]) + y, x = [2.0, 1.0], [1.0, 2.0] + objective = normal if kind == "normal" else lambda r, t: formula(r, t, x) + _, g, metric = objective(raw, y) + eps = 1e-5 + for row in range(2): + for channel in range(2): + delta = np.zeros_like(raw) + delta[row, channel] = eps + assert (objective(raw + delta, y)[0] - objective(raw - delta, y)[0]) / ( + 2 * eps + ) * 2 == pytest.approx(g[row, channel], abs=1e-8) + if kind == "formula": + jacobian = np.column_stack( + [ + ( + formula_predict(raw + np.eye(2)[k] * eps, x)[0] + - formula_predict(raw - np.eye(2)[k] * eps, x)[0] + ) + / (2 * eps) + for k in range(2) + ] + ) + np.testing.assert_allclose(metric, np.einsum("ni,nj->nij", jacobian, jacobian), atol=1e-10) + assert all(abs(np.linalg.det(m)) < 1e-14 for m in metric) + + +def test_formula_direction_solve_and_rank_deficiency(): + raw = np.zeros((1, 2)) + _, g, metric = formula(raw, [2], [1]) + damping = 0.2 + full = directions(g, metric, mode="full", damping=damping) + np.testing.assert_allclose((metric[0] + damping * np.eye(2)) @ full[0], -g[0]) + diagonal = directions(g, metric, mode="diagonal", damping=damping) + np.testing.assert_allclose(diagonal, -g / (np.diagonal(metric, axis1=1, axis2=2) + damping)) + assert not np.allclose(full, diagonal) + with pytest.raises(ValueError): + directions(g, metric, mode="full") + + +def test_formula_monotonicity_initialization_and_nonidentifiability(): + base = formula_base([2, 4], weight=[1, 3]) + prediction, a, b = formula_predict(np.tile(base, (3, 1)), [0.5, 1, 2]) + np.testing.assert_allclose(a, 3.5) + np.testing.assert_allclose(b, 1) + assert np.all(np.diff(prediction) > 0) + + # Distinct a,b fit the same one-age observation exactly. + def inverse(v): + return math.log(math.expm1(v)) + + alternatives = [[inverse(2), inverse(math.log(2))], [inverse(3), inverse(-math.log(2 / 3))]] + np.testing.assert_allclose(formula_predict(alternatives, [1, 1])[0], [1, 1]) + # A decreasing pair at the same recipe Z cannot fit this increasing family. + predictions = formula_predict([base, base], [1, 2])[0] + assert np.sum((predictions - [3, 1]) ** 2) >= 2 + + +def test_normal_evaluator_independent_nll_and_crps(): + raw = [[0, 0], [1, math.log(2)]] + nll, crps = normal_scores(raw, [0, 1], weight=[1, 3]) + expected = (math.sqrt(2) - 1) / math.sqrt(math.pi) + assert crps == pytest.approx(expected * 7 / 4) + assert nll == pytest.approx(0.5 * math.log(2 * math.pi) + 0.75 * math.log(2)) + + +@pytest.mark.parametrize("mode", ["ordinary", "diagonal", "full"]) +def test_formula_two_round_joint_and_validation_reconstruction(mode): + bins, x = [[0], [0], [1], [1]], [1.0, 2.0, 1.0, 2.0] + truth = np.array([[2.0, 1.0], [2.0, 1.0], [3.0, 0.5], [3.0, 0.5]]) + raw_true = np.log(np.expm1(truth)) + y = formula_predict(raw_true, x)[0] + raw = np.zeros((4, 2)) + reconstructed = raw.copy() + previous = None + + def objective(r, t, weight=None): + return formula(r, t, x, weight=weight) + + for _ in range(2): + _, g, metric = objective(raw, y) + result = step(bins, raw, y, objective, mode=mode, damping=0.1, rates=(1.0, 0.5, 0.1)) + assert result.accepted + assert result.loss_after < result.loss_before + if previous is not None: + assert not np.allclose(g, previous) + # Every tree targets the direction from the SAME snapshot. + direction = directions(g, metric, mode=mode, damping=0.1) + for channel, tree, coefficient in result.terms: + for node in tree.nodes: + if node.condition is None: + assert node.value == pytest.approx( + sum(direction[list(node.rows), channel]) / (1 + len(node.rows)) + ) + reconstructed[:, channel] += coefficient * tree.predict(bins) + raw = np.array(result.raw_after) + np.testing.assert_allclose(reconstructed, raw) + previous = g.copy() + + +def test_normal_two_round_natural_root_hand_updates_and_weights(): + y, raw = [1.0, 3.0], np.zeros((2, 2)) + for _ in range(2): + mu, ell = raw[0] + expected_mu = (7 - 3 * mu) / 4 + expected_scale = (0.5 * ((1 - mu) ** 2 + 2 * (3 - mu) ** 2) * math.exp(-2 * ell) - 1.5) / 4 + result = step( + [[0], [0]], + raw, + y, + normal, + weight=[1, 2], + mode="full", + rates=(0.1,), + require_decrease=False, + ) + assert result.accepted + np.testing.assert_allclose( + np.array(result.raw_after)[0], raw[0] + 0.1 * np.array([expected_mu, expected_scale]) + ) + raw = np.array(result.raw_after) + + +def test_ordered_recomputes_geometry_and_differs_from_joint(): + bins, joint_raw, y = [[0], [0]], np.zeros((2, 2)), [1.0, 3.0] + ordered_raw = joint_raw.copy() + for _ in range(2): + joint = step(bins, joint_raw, y, normal, mode="full", rates=(0.1,), require_decrease=False) + first = step( + bins, + ordered_raw, + y, + normal, + mode="full", + channels=(0,), + rates=(0.1,), + require_decrease=False, + ) + second = step( + bins, + first.raw_after, + y, + normal, + mode="full", + channels=(1,), + rates=(0.1,), + require_decrease=False, + ) + assert not np.allclose(second.raw_after, joint.raw_after) + np.testing.assert_allclose( + np.array(second.raw_after)[:, 0], np.array(joint.raw_after)[:, 0] + ) + joint_raw, ordered_raw = np.array(joint.raw_after), np.array(second.raw_after) + + +def test_rejected_candidate_preserves_snapshot_and_has_no_terms(): + raw = np.zeros((2, 2)) + original = raw.copy() + result = step([[0], [0]], raw, [1.0, 3.0], normal, mode="full", rates=(1000.0,)) + assert not result.accepted and result.terms == () + assert result.raw_before == result.raw_after + np.testing.assert_array_equal(raw, original) + raw[:] = 8 + np.testing.assert_array_equal(result.raw_after, original) + + +def test_backtracking_records_rejections_and_commits_only_accepted_coefficient(): + result = step( + [[0], [0]], np.zeros((2, 2)), [1.0, 3.0], normal, mode="full", rates=(1000.0, 5.0, 0.1) + ) + assert result.accepted + assert len(result.trials) == 3 + assert result.trials[0][1] is None + assert result.trials[1][1] > result.loss_before + assert all(coefficient == 0.1 for _, _, coefficient in result.terms) + direct = step([[0], [0]], np.zeros((2, 2)), [1.0, 3.0], normal, mode="full", rates=(0.1,)) + np.testing.assert_allclose(result.raw_after, direct.raw_after) + + +def test_direction_fit_weight_replication_and_evaluator_agreement(): + raw = np.array([[0.1, -0.2], [0.3, 0.5]]) + y = np.array([1.0, 3.0]) + weighted = step([[0], [1]], raw, y, normal, weight=[1, 3], rates=(0.1,)) + ids = [0, 1, 1, 1] + repeated = step([[0], [1], [1], [1]], raw[ids], y[ids], normal, rates=(0.1,)) + np.testing.assert_allclose(np.array(weighted.raw_after)[ids], repeated.raw_after) + assert normal_scores(raw, y, weight=[1, 3])[0] == pytest.approx( + normal(raw, y, weight=[1, 3])[0] + ) + + +@pytest.mark.parametrize("rates", [(), (0,), (-1,), (np.nan,)]) +def test_invalid_step_coefficients(rates): + with pytest.raises(ValueError): + step([[0]], [[0, 0]], [1], normal, rates=rates) + + +@pytest.mark.parametrize("x", [[0], [-1], [np.nan], []]) +def test_invalid_formula_structure(x): + with pytest.raises(ValueError): + formula([[0, 0]], [1], x) + + +def test_formula_small_positive_parameters_use_stable_expm1(): + prediction, a, b = formula_predict([[-30, -30]], [1e-6]) + assert prediction[0] > 0 + assert prediction[0] == pytest.approx(a[0] * b[0] * 1e-6, rel=1e-12, abs=0) + + +def test_bad_metric_damping_and_normal_initialization_rejected(): + with pytest.raises(ValueError): + directions([[1, 1]], [[[1, 0], [0, 1]]], damping=-1) + with pytest.raises(ValueError): + directions([[1, 1]], [[[1, 2], [0, 1]]], mode="full") + with pytest.raises(ValueError): + normal_base([1, 1], minimum_scale=0) + + +def test_formula_rejects_underflowed_positive_parameters(): + with pytest.raises(ValueError): + formula_predict([[-1000, 0]], [1]) diff --git a/tests/v1/test_current_worker.py b/tests/v1/test_current_worker.py new file mode 100644 index 0000000..e558735 --- /dev/null +++ b/tests/v1/test_current_worker.py @@ -0,0 +1,287 @@ +"""Current evaluation worker contracts, separate from baseline implementations.""" + +import importlib +import json +import subprocess +import sys +from pathlib import Path + +import numpy as np +import pytest +from benchmarks.v1.openboost_predict import predict_saved +from benchmarks.v1.openboost_worker import fit + +from openboost import NumericData, Problem, RunContext +from openboost.objectives import Normal +from openboost.recipes import normal, squared + + +def test_current_worker_is_available(): + assert callable(importlib.import_module("benchmarks.v1.openboost_worker").fit) + + +def fixture(app="A1"): + x = np.arange(12, dtype=float)[:, None] + arrays = dict( + x_train=x[:8], + y_train=np.sin(x[:8, 0]), + x_validation=x[8:], + y_validation=np.sin(x[8:, 0]), + validation_row_ids=np.arange(8, 12), + weight_train=np.arange(8, dtype=float), + weight_validation=np.ones(4), + ) + job = dict( + application=app, + library="openboost", + device="cpu", + threads=1, + seed=7, + config=dict(rounds=3, learning_rate=0.1, bins=8), + ) + return job, arrays + + +@pytest.mark.parametrize("app", ["A1", "A11"]) +@pytest.mark.parametrize("patience", [None, 1]) +def test_direct_recipe_parity_and_fresh_prediction(app, patience, tmp_path): + job, arrays = fixture(app) + job["early_stopping_rounds"] = patience + prediction, saved, training = fit(job, arrays) + problems = [] + for part in ("train", "validation"): + x, y = arrays["x_" + part], arrays["y_" + part] + data = NumericData(x, np.arange(len(x)), ("x0",)) + problems.append( + Problem( + data, + y[:, None], + data.row_ids, + weight=arrays["weight_" + part], + raw_width=1 if app == "A1" else 2, + ) + ) + direct = (squared if app == "A1" else normal)( + *problems, context=RunContext("evaluation", 7), patience=patience, **job["config"] + ) + model = direct.state.model if patience is None else direct.state.best_model + raw = model.predict(problems[1].data) + expected = raw[:, 0] if app == "A1" else Normal.parameters(raw) + np.testing.assert_array_equal(prediction, expected) + assert training["selected_model_identity"] == model.identity + assert training["stop"]["completed_rounds"] == direct.stop.completed_rounds + model_path = tmp_path / "model.bin" + model_path.write_text(json.dumps(saved)) + np.savez( + tmp_path / "features.npz", x=arrays["x_validation"], row_ids=arrays["validation_row_ids"] + ) + script = Path(__file__).resolve().parents[2] / "benchmarks/v1/openboost_predict.py" + subprocess.run( + [ + sys.executable, + str(script), + str(model_path), + str(tmp_path / "features.npz"), + str(tmp_path / "result.npz"), + ], + cwd=tmp_path, + check=True, + ) + with np.load(tmp_path / "result.npz") as result: + np.testing.assert_array_equal(result["prediction"], prediction) + np.testing.assert_array_equal(result["row_ids"], arrays["validation_row_ids"]) + + +@pytest.mark.parametrize( + "bad", ["test", "validation", "weights", "ids", "width", "config", "device", "threads", "task"] +) +def test_unsupported_or_contaminated_inputs_fail(bad): + job, arrays = fixture() + if bad == "test": + arrays["y_test"] = np.zeros(2) + elif bad == "validation": + arrays.pop("y_validation") + elif bad == "weights": + arrays["weight_train"][0] = -1 + elif bad == "ids": + arrays["validation_row_ids"] = np.zeros(4) + elif bad == "width": + arrays["x_validation"] = np.ones((4, 2)) + elif bad == "config": + job["config"]["subsample"] = 0.5 + elif bad == "device": + job["device"] = "cuda" + elif bad == "threads": + job["threads"] = 2 + else: + job["application"] = "A5" + with pytest.raises(ValueError): + fit(job, arrays) + + +def test_saved_output_semantics_are_validated(): + job, arrays = fixture("A11") + _, saved, _ = fit(job, arrays) + saved["output"] = "mean" + with pytest.raises(ValueError): + predict_saved(saved, arrays["x_validation"]) + + +def test_a6_train_scale_and_original_units(): + job, arrays = fixture("A6") + arrays["y_train"] = np.column_stack([100 + 20 * arrays["y_train"], np.full(8, 7.0)]) + arrays["y_validation"] = np.column_stack([100 + 20 * arrays["y_validation"], np.full(4, 7.0)]) + prediction, saved, training = fit(job, arrays) + assert prediction.shape == (4, 2) + np.testing.assert_allclose(prediction[:, 1], 7.0) + assert training["target_scale"]["constant"] == [False, True] + + +@pytest.mark.parametrize("mode", ["shared", "independent"]) +@pytest.mark.parametrize("patience", [None, 1]) +def test_a6_scaled_selection_and_fresh_replay(mode, patience, tmp_path): + from benchmarks.v1.preprocessing import fit_target_scale + + from openboost.multioutput import MultiOutputModel, TargetScale + from openboost.recipes import multi_squared + + job, arrays = fixture("A6") + job["config"]["mode"] = mode + job["early_stopping_rounds"] = patience + for part in ("train", "validation"): + y = arrays["y_" + part] + arrays["y_" + part] = np.column_stack([100 + 20 * y, -300 + 0.01 * y, np.full(len(y), 7.0)]) + # Large validation shift must not alter train-fitted scale. + arrays["y_validation"][:, 0] += 1000 + expected_scale = fit_target_scale(arrays["y_train"]) + scale = TargetScale(expected_scale["mean"], expected_scale["std"], expected_scale["constant"]) + problems = [] + for part in ("train", "validation"): + data = NumericData(arrays["x_" + part], np.arange(len(arrays["x_" + part])), ("x0",)) + p = Problem( + data, arrays["y_" + part], data.row_ids, raw_width=3, weight=arrays["weight_" + part] + ) + problems.append(scale.transform(p)) + direct = multi_squared( + *problems, context=RunContext("evaluation", 7), patience=patience, **job["config"] + ) + prediction, saved, training = fit(job, arrays) + assert training["target_scale"] == expected_scale == saved["target_scale"] + assert training["best_validation_score"] == direct.state.best_score + model = direct.state.model if patience is None else direct.state.best_model + np.testing.assert_array_equal( + prediction, MultiOutputModel(model, scale).predict(problems[1].data) + ) + model_path = tmp_path / "model.bin" + model_path.write_text(json.dumps(saved)) + packet = tmp_path / "features.npz" + np.savez(packet, x=arrays["x_validation"], row_ids=arrays["validation_row_ids"]) + script = Path(__file__).resolve().parents[2] / "benchmarks/v1/openboost_predict.py" + subprocess.run( + [sys.executable, str(script), str(model_path), str(packet), str(tmp_path / "replay.npz")], + cwd=tmp_path, + check=True, + ) + with np.load(tmp_path / "replay.npz") as replay: + np.testing.assert_array_equal(replay["prediction"], prediction) + saved["target_scale"]["std"][0] = 0 + with pytest.raises(ValueError): + predict_saved(saved, arrays["x_validation"]) + + +def test_binary_worker_probabilities(): + job, arrays = fixture("A2") + job["classes"] = 2 + arrays["y_train"] = np.arange(8) % 2 + arrays["y_validation"] = np.arange(4) % 2 + prediction, saved, _ = fit(job, arrays) + assert prediction.shape == (4,) + assert np.all((prediction >= 0) & (prediction <= 1)) + assert saved["output"] == "positive_class_probability" + + +@pytest.mark.parametrize("app, count", [("A2", 2), ("A3", 3)]) +@pytest.mark.parametrize("patience", [None, 1]) +def test_classification_direct_and_fresh_probability_order(app, count, patience, tmp_path): + from openboost import ClassSchema + from openboost.recipes import binary, multiclass + + job, arrays = fixture(app) + job.update(classes=count, early_stopping_rounds=patience) + arrays["validation_row_ids"] = np.array([f"validation:{i}" for i in range(4)]) + problems = [] + for part in ("train", "validation"): + x = arrays["x_" + part] + arrays["y_" + part] = np.arange(len(x)) % count + data = NumericData(x, np.arange(len(x)), ("x0",)) + problems.append( + Problem( + data, + arrays["y_" + part][:, None], + data.row_ids, + classes=ClassSchema(tuple(range(count))), + raw_width=1 if app == "A2" else count, + weight=arrays["weight_" + part], + ) + ) + direct = (binary if app == "A2" else multiclass)( + *problems, context=RunContext("evaluation", 7), patience=patience, **job["config"] + ) + prediction, saved, training = fit(job, arrays) + model = direct.state.model if patience is None else direct.state.best_model + probability = model.predict_proba(problems[1].data) + np.testing.assert_array_equal(prediction, probability[:, 1] if app == "A2" else probability) + np.testing.assert_allclose(probability.sum(axis=1), 1.0) + assert training["class_order"] == list(range(count)) + assert training["best_validation_score"] == direct.state.best_score + model_path = tmp_path / "model.bin" + model_path.write_text(json.dumps(saved)) + packet = tmp_path / "features.npz" + np.savez(packet, x=arrays["x_validation"], row_ids=arrays["validation_row_ids"]) + script = Path(__file__).resolve().parents[2] / "benchmarks/v1/openboost_predict.py" + subprocess.run( + [sys.executable, str(script), str(model_path), str(packet), str(tmp_path / "replay.npz")], + cwd=tmp_path, + check=True, + ) + with np.load(tmp_path / "replay.npz") as replay: + np.testing.assert_array_equal(replay["prediction"], prediction) + np.testing.assert_array_equal(replay["row_ids"], arrays["validation_row_ids"]) + saved["model"]["classes"] = list(reversed(saved["model"]["classes"])) + with pytest.raises(ValueError): + predict_saved(saved, arrays["x_validation"]) + + +@pytest.mark.parametrize( + "bad", + [ + "missing_count", + "bool_count", + "wrong_count", + "fractional", + "range", + "missing_class", + "foreign_option", + ], +) +def test_classification_invalid_contracts_fail(bad): + job, arrays = fixture("A3") + job["classes"] = 3 + arrays["y_train"] = np.arange(8) % 3 + arrays["y_validation"] = np.arange(4) % 3 + if bad == "missing_count": + job.pop("classes") + elif bad == "bool_count": + job["classes"] = True + elif bad == "wrong_count": + job["classes"] = 2 + elif bad == "fractional": + arrays["y_train"] = arrays["y_train"].astype(float) + 0.5 + elif bad == "range": + arrays["y_validation"][0] = 3 + elif bad == "missing_class": + arrays["y_train"][:] = 0 + else: + job["config"]["mode"] = "natural" + with pytest.raises(ValueError): + fit(job, arrays) diff --git a/tests/v1/test_data_reference.py b/tests/v1/test_data_reference.py new file mode 100644 index 0000000..b8808aa --- /dev/null +++ b/tests/v1/test_data_reference.py @@ -0,0 +1,110 @@ +"""Hand-derived training-only transformation cases for the future data layer.""" + +import numpy as np +import pytest + +from .reference.data import CategoryMap, NumericBinning + + +def test_linear_cuts_and_boundary_routing_are_training_only(): + values = np.array([0.0, 2.0, 4.0, 6.0]) + fitted = NumericBinning.fit(values, bins=4) + assert fitted.cuts == (1.5, 3.0, 4.5) + values[:] = 100 + codes, missing = fitted.transform([-100, 1.5, 3, 4.5, 100, np.nan]) + assert codes == (0, 0, 1, 2, 3, 0) + assert missing == (False, False, False, False, False, True) + assert fitted.cuts == (1.5, 3.0, 4.5) + + +@pytest.mark.parametrize( + "values,bins,cuts", + [ + ([0, 0, 0, 1], 2, (0.0,)), + ([2, 2, 2], 4, ()), + ([np.nan, np.nan], 4, ()), + ([0, 1], 1, ()), + ([0, 0, 0, 1], 4, (0.0, 0.25)), + ], +) +def test_degenerate_numeric_columns(values, bins, cuts): + assert NumericBinning.fit(values, bins=bins).cuts == cuts + + +@pytest.mark.parametrize("values", [[np.inf], [-np.inf], [], [[1, 2]]]) +def test_invalid_numeric_training(values): + with pytest.raises(ValueError): + NumericBinning.fit(values) + + +@pytest.mark.parametrize("bins", [0, -1, 1.5, True]) +def test_invalid_bin_budget(bins): + with pytest.raises(ValueError): + NumericBinning.fit([0, 1], bins=bins) + + +def test_numeric_transform_rejects_inf_and_interpolation_does_not_overflow(): + fitted = NumericBinning.fit([-1e308, 1e308], bins=2) + assert fitted.cuts == (0.0,) + with pytest.raises(ValueError): + fitted.transform([np.inf]) + + +def test_categories_are_stable_training_only_and_route_by_equality(): + values = ["z", "a", "m", None, "a"] + fitted = CategoryMap.fit(values) + assert fitted.values == ("a", "m", "z") + values[0] = "new" + codes, missing = fitted.transform(["z", "a", "m", "unseen", None, float("nan")]) + assert codes == (2, 0, 1, 0, 0, 0) + assert missing == (False, False, False, True, True, True) + assert fitted.route(["a", "m", "z", None, "unseen"], "m", missing_left=True) == ( + False, + True, + False, + True, + True, + ) + assert fitted.route(["a", "m", "z", None], "m", missing_left=False) == ( + False, + True, + False, + False, + ) + assert fitted.values == ("a", "m", "z") + + +def test_category_types_are_not_silently_coerced(): + assert CategoryMap.fit([10, -2, 10]).values == (-2, 10) + assert CategoryMap.fit([None, np.nan]).values == () + for values in ([1, "1"], [True, 1], [1.5], [["a"]]): + with pytest.raises(ValueError): + CategoryMap.fit(values) + with pytest.raises(ValueError): + CategoryMap.fit(["a"]).route(["a"], "unknown", missing_left=False) + + +def test_middle_category_candidate_cannot_be_replaced_by_numeric_threshold(): + from .reference.scalar import node_score + + tokens = ["a", "a", "m", "m", "z", "z"] + gradient = np.array([1.0, 1.0, -2.0, -2.0, 1.0, 1.0]) + fitted = CategoryMap.fit(tokens) + gains = {} + for category in fitted.values: + left = np.array(fitted.route(tokens, category, missing_left=False)) + gains[category] = ( + node_score(sum(gradient[left]), sum(left)) + + node_score(sum(gradient[~left]), sum(~left)) + - node_score(sum(gradient), len(tokens)) + ) + assert max(gains, key=gains.get) == "m" + assert gains["m"] == pytest.approx(64 / 15) + # Neither threshold on sorted codes can isolate the middle category. + codes = np.array(fitted.transform(tokens)[0]) + for threshold in (0, 1): + left = codes <= threshold + gain = node_score(sum(gradient[left]), sum(left)) + node_score( + sum(gradient[~left]), sum(~left) + ) + assert gain < gains["m"] diff --git a/tests/v1/test_evaluation_data.py b/tests/v1/test_evaluation_data.py new file mode 100644 index 0000000..1ebd609 --- /dev/null +++ b/tests/v1/test_evaluation_data.py @@ -0,0 +1,82 @@ +"""Independent partition and task-field counterexamples for evaluation data.""" + +import numpy as np +import pytest +from benchmarks.v1.real_data import group_splits, stratified_splits + + +def test_groups_never_cross_partitions_and_global_rng_is_untouched(): + groups = np.repeat(np.arange(10), np.arange(1, 11)) + before = np.random.get_state() + for seed in range(5): + split = group_splits(groups, seed) + assert [len(set(groups[r])) for r in split] == [6, 2, 2] + assert set(groups[split[0]]).isdisjoint(groups[split[1]]) + assert set(groups[split[0]]).isdisjoint(groups[split[2]]) + assert set(groups[split[1]]).isdisjoint(groups[split[2]]) + np.testing.assert_array_equal(np.sort(np.concatenate(split)), np.arange(len(groups))) + after = np.random.get_state() + np.testing.assert_array_equal(before[1], after[1]) + assert before[2:] == after[2:] + + +def test_stratification_preserves_every_class_and_row(): + labels = np.repeat(np.arange(3), 10) + split = stratified_splits(labels, 0) + for rows, expected in zip(split, [6, 2, 2], strict=True): + assert np.bincount(labels[rows]).tolist() == [expected] * 3 + assert len(set(np.concatenate(split))) == 30 + + +@pytest.mark.parametrize("groups", [[], [1, 1], [float("nan"), 1, 2]]) +def test_invalid_group_splits(groups): + with pytest.raises(ValueError): + group_splits(groups, 0) + + +def test_insurance_join_retains_only_declared_targets(monkeypatch): + from benchmarks.v1 import real_data as rd + + freq = b"@data\n1,0,0.5,'A',4,1,30,50,'B1','Regular',10,'R11'\n2,1,1,'A',4,1,30,50,'B1','Regular',10,'R11'\n3,1,2,'A',4,1,30,50,'B1','Regular',10,'R11'\n4,0,1,'A',4,1,30,50,'B1','Regular',10,'R11'\n" + sev = b"@data\n2,10\n2,20\n2,-3\n4,5\n999,10\n" + monkeypatch.setattr(rd, "source", lambda root, name: freq if name == "freq" else sev) + data, audit = rd.insurance(".") + np.testing.assert_array_equal(data["paid_total"], [0, 30, 0, 5]) + np.testing.assert_array_equal(data["paid_count"], [0, 2, 0, 1]) + np.testing.assert_array_equal(data["aggregate_eligible"], [True, True, False, False]) + np.testing.assert_array_equal(data["severity_policy_row"], [1, 1, 3]) + assert audit["orphan_claims"] == 1 and audit["nonpositive_claims"] == 1 + assert data["x"].shape == (4, 5) # No ID, count, exposure, or payment target in X. + + +def test_parkinsons_excludes_subject_and_both_targets(monkeypatch): + from benchmarks.v1 import real_data as rd + + row = ",".join(str(i) for i in range(22)) + monkeypatch.setattr( + rd, "source", lambda *args: {"parkinsons_updrs.data": ("header\n" + row).encode()} + ) + data, rule = rd.load(".", "parkinsons") + assert rule == "group" + np.testing.assert_array_equal(data["y"], [[4, 5]]) + np.testing.assert_array_equal(data["x"][0], [1, 2, 3, *range(6, 22)]) + + +def test_survival_event_is_not_an_input_or_death_imputation(monkeypatch): + from benchmarks.v1 import real_data as rd + + raw = b"@data\nstandard,adeno,20,censored,50,2,60,no\ntest,large,30,dead,40,3,61,yes\n" + monkeypatch.setattr(rd, "source", lambda *args: raw) + data, rule = rd.load(".", "veteran") + assert rule == "event" + np.testing.assert_array_equal(data["event"], [0, 1]) + np.testing.assert_array_equal(data["y"], [20, 30]) + assert data["x"].shape == (2, 6) + + +def test_source_corruption_rejected(tmp_path): + from benchmarks.v1.real_data import source + + (tmp_path / "covertype.zip").write_bytes(b"bad") + with pytest.raises(ValueError, match="hash"): + source(tmp_path, "covertype") diff --git a/tests/v1/test_evaluation_preprocessing.py b/tests/v1/test_evaluation_preprocessing.py new file mode 100644 index 0000000..6de51b2 --- /dev/null +++ b/tests/v1/test_evaluation_preprocessing.py @@ -0,0 +1,26 @@ +import numpy as np +import pytest +from benchmarks.v1.preprocessing import censoring_support, fit_encoder, fit_target_scale, transform + + +def test_test_categories_and_outliers_cannot_change_training_encoder(): + x = np.array([[1, np.nan], [3, np.nan]]) + enc = fit_encoder(x, {"kind": ["a", None]}) + transformed = transform(enc, [[100, 7]], {"kind": ["unseen"]}) + assert enc["median"] == [2, 0] + assert enc["categories"] == {"kind": ["a"]} + np.testing.assert_array_equal(transformed, [[100, 7, 0, 0, 0, 1]]) + with pytest.raises(ValueError): + transform(enc, [[1, 2]], {}) + + +def test_scaling_keeps_constant_output_invertible(): + scale = fit_target_scale([[1, 4], [3, 4]]) + assert scale == {"mean": [2.0, 4.0], "std": [1.0, 1.0], "constant": [False, True]} + + +def test_censoring_ties_and_supported_grid(): + r = censoring_support([1, 1, 2, 3], [1, 0, 1, 0]) + # Four at risk at t=1, one event removed, one censor among three. + assert r["survival"][0] == pytest.approx(2 / 3) + assert r["survival"][-1] == 0 and max(r["grid"]) < 3 diff --git a/tests/v1/test_extended_reference.py b/tests/v1/test_extended_reference.py new file mode 100644 index 0000000..9248aaa --- /dev/null +++ b/tests/v1/test_extended_reference.py @@ -0,0 +1,236 @@ +"""Independent mathematical counterexamples for A4/A5/A6.""" + +import numpy as np +import pytest + +from .reference.quantile import fit_quantile_tree, pinball, weighted_quantile +from .reference.ranking import pairwise, query_ndcg +from .reference.tree import fit_tree +from .reference.vector import TargetScale, fit_vector_stump + + +def test_pair_hand_normalization_and_query_isolation(): + result = pairwise( + [0, 0, 0, 0], [2, 0, 1, 1], ["a", "a", "b", "b"], query_weight={"a": 3, "b": 7} + ) + assert result.loss == pytest.approx(3 * np.log(2)) + np.testing.assert_allclose(result.gradient, [-1.5, 1.5, 0, 0]) + np.testing.assert_allclose(result.curvature, [0.75, 0.75, 0, 0]) + assert result.pairs == ((0, 1, 3.0),) + assert pairwise([0, 0, 0], [2, 1, 0], [0, 0, 0]).loss == pytest.approx(np.log(2)) + + +def test_pair_gradient_finite_difference_shift_and_weights(): + raw = np.array([0.2, -0.4, 1.1, 2.0]) + rel = [2, 0, 1, 0] + groups = [0, 0, 1, 1] + opts = {"query_weight": {0: 2.0, 1: 0.5}, "pair_weight": {(0, 1): 3.0, (2, 3): 2.0}} + result = pairwise(raw, rel, groups, **opts) + for i in range(4): + delta = np.eye(4)[i] * 1e-5 + plus, minus = ( + pairwise(raw + delta, rel, groups, **opts), + pairwise(raw - delta, rel, groups, **opts), + ) + assert (plus.loss - minus.loss) / 2e-5 == pytest.approx(result.gradient[i], abs=1e-9) + assert (plus.gradient[i] - minus.gradient[i]) / 2e-5 == pytest.approx( + result.curvature[i], abs=1e-9 + ) + shifted = pairwise(raw + [100, 100, -30, -30], rel, groups, **opts) + np.testing.assert_allclose(result.gradient, shifted.gradient) + assert result.gradient.sum() == pytest.approx(0) + with pytest.raises(ValueError): + pairwise(raw, rel, groups, weight=np.ones(4)) + + +def test_ndcg_ties_and_zero_ideal(): + value = query_ndcg([0, 0], [0, 1], row_ids=[20, 10], k=1) + assert value == 1 + assert query_ndcg([0, 0], [0, 1], row_ids=[10, 20], k=1) == 0 + assert query_ndcg([1, 0], [0, 0]) == 1 + + +def test_lambda_weights_change_with_new_ranking(): + first = pairwise([0, 0, 0], [0, 2, 1], [0, 0, 0], lambdas=True, k=1) + second = pairwise([0, 2, 1], [0, 2, 1], [0, 0, 0], lambdas=True, k=1) + # At k=1 swapping two rows outside the top position has zero NDCG delta. + assert dict(((i, j), w) for i, j, w in first.pairs)[(1, 2)] == 0 + assert dict(((i, j), w) for i, j, w in second.pairs)[(1, 2)] == pytest.approx(2 / 9) + + +def test_pair_two_rounds_recompute_scores(): + raw = np.zeros(2) + for step in range(2): + result = pairwise(raw, [1, 0], [0, 0]) + tree = fit_tree([[0], [1]], result.gradient, result.curvature, max_depth=1) + q = 0.5 if step == 0 else 1 / (1 + np.exp(0.08)) + value = q / (1 + q * (1 - q)) + np.testing.assert_allclose(tree.predict([[0], [1]]), [value, -value]) + raw += 0.1 * tree.predict([[0], [1]]) + assert raw[0] > 0 > raw[1] + + +@pytest.mark.parametrize("q,expected", [(0.1, 0), (0.5, 2), (0.9, 10)]) +def test_weighted_quantile_hand_and_optimality(q, expected): + residual = np.array([0.0, 2.0, 10.0]) + weight = [1, 3, 1] + value = weighted_quantile(residual, q, weight) + assert value == expected + optimum = pinball(np.full(3, value), residual, q, weight)[0] + for candidate in [-1, 0, 1, 2, 5, 10, 11]: + assert pinball(np.full(3, candidate), residual, q, weight)[0] >= optimum - 1e-14 + assert weighted_quantile([-100, 0, 2], 0.5, [0, 1, 1]) == 0 + + +def test_quantile_two_rounds_uses_residual_leaf_not_newton(): + y = np.array([0.0, 2.0, 10.0]) + raw = np.full(3, weighted_quantile(y, 0.5, [2, 1, 2])) + for step in range(2): + tree = fit_quantile_tree([[0], [0], [1]], raw, y, 0.5, weight=[2, 1, 2], max_depth=1) + assert tree.predict([[1]])[0] == pytest.approx(8 if step == 0 else 7.2) + raw += 0.1 * tree.predict([[0], [0], [1]]) + assert raw[-1] == pytest.approx(3.52) + + +def test_vector_shared_topology_and_projection_keep_full_leaves(): + x = [[0, 0], [0, 1], [1, 0], [1, 1]] + y = np.array([[-1.0, -3.0], [-1.0, 3.0], [1.0, -3.0], [1.0, 3.0]]) + shared = fit_vector_stump(x, np.zeros_like(y), y) + projected = fit_vector_stump(x, np.zeros_like(y), y, projection=[[1.0], [0.0]]) + assert shared.condition.feature == 1 + assert projected.condition.feature == 0 + np.testing.assert_allclose(shared.predict(x), [[0, -2], [0, 2], [0, -2], [0, 2]]) + assert projected.predict(x).shape == (4, 2) + np.testing.assert_allclose( + projected.predict(x), [[-2 / 3, 0], [-2 / 3, 0], [2 / 3, 0], [2 / 3, 0]] + ) + independent = [fit_tree(x, -y[:, k], np.ones(4), max_depth=1) for k in range(2)] + assert [t.nodes[0].condition.feature for t in independent] == [0, 1] + + +def test_vector_k1_and_two_round_permutation(): + x = [[0], [0], [1], [1]] + y = np.array([[-2.0, 1.0], [-2.0, 1.0], [2.0, -1.0], [2.0, -1.0]]) + raw = np.zeros_like(y) + for step in range(2): + tree = fit_vector_stump(x, raw, y) + permuted = fit_vector_stump(x, raw[:, ::-1], y[:, ::-1]) + np.testing.assert_allclose(tree.predict(x)[:, ::-1], permuted.predict(x)) + expected = np.array([-4 / 3, 2 / 3]) if step == 0 else np.array([-56 / 45, 28 / 45]) + np.testing.assert_allclose(tree.predict([[0]])[0], expected) + scalar = fit_tree(x, raw[:, 0] - y[:, 0], np.ones(4), max_depth=1) + single = fit_vector_stump(x, raw[:, :1], y[:, :1]) + np.testing.assert_allclose(single.predict(x)[:, 0], scalar.predict(x)) + raw += 0.1 * tree.predict(x) + np.testing.assert_allclose(raw[0], [-58 / 225, 29 / 225]) + + +def test_target_scaling_training_only_and_constant_axis(): + y = np.array([[0.0, 5.0], [2.0, 5.0]]) + scale = TargetScale.fit(y) + y[:] = -100 + np.testing.assert_allclose(scale.transform([[0, 5], [2, 5]]), [[-1, 0], [1, 0]]) + np.testing.assert_allclose(scale.inverse([[9, 0]]), [[10, 5]]) + assert scale.constant == (False, True) + + +def test_quantile_ties_may_have_no_positive_pseudo_split(): + # The residual quantile does not imply the pseudo-Newton split is profitable. + tree = fit_quantile_tree( + [[0], [0], [1]], [2, 2, 2], [0, 2, 10], 0.5, weight=[1, 3, 1], max_depth=1 + ) + assert tree.nodes[0].condition is None + np.testing.assert_array_equal(tree.predict([[0], [1]]), [0, 0]) + + +@pytest.mark.parametrize("q", [0, 1, -0.1, np.nan]) +def test_quantile_rejects_invalid_level(q): + with pytest.raises(ValueError): + weighted_quantile([0, 1], q) + + +def test_quantile_zero_weights_and_replication(): + for weight in ([0, 0], [-1, 2], [1, np.inf]): + with pytest.raises(ValueError): + weighted_quantile([0, 1], 0.5, weight) + for q in (0.1, 0.5, 0.9): + assert weighted_quantile([0, 2, 10], q, [1, 3, 1]) == weighted_quantile([0, 2, 2, 2, 10], q) + loss, g, h = pinball([0, 3, 10], [0, 2, 10], 0.9, [1, 3, 1]) + assert loss == pytest.approx(0.06) + np.testing.assert_allclose(g, [-0.9, 0.1, -0.9]) + np.testing.assert_array_equal(h, [1, 1, 1]) # named pseudo curvature + + +def test_ranking_rejects_misaligned_or_silently_unused_metadata(): + bad = [ + dict(query_weight={9: 1}), + dict(pair_weight={(1, 0): 1}), + dict(query_weight={0: -1}), + dict(row_ids=[1, 1]), + dict(k=0), + ] + for options in bad: + with pytest.raises(ValueError): + pairwise([0, 0], [1, 0], [0, 0], **options) + for relevance in ([1, -0.5], [np.nan, 0], [1024, 0]): + with pytest.raises(ValueError): + pairwise([0, 0], relevance, [0, 0]) + with pytest.raises(ValueError): + pairwise([0, 0], [1, 0], [0]) + + +def test_lambda_permutation_uses_stable_row_ids(): + raw, rel, ids = np.zeros(3), np.array([2, 0, 1]), np.array([30, 10, 20]) + original = pairwise(raw, rel, [0, 0, 0], row_ids=ids, lambdas=True) + perm = [2, 0, 1] + changed = pairwise(raw[perm], rel[perm], [0, 0, 0], row_ids=ids[perm], lambdas=True) + assert changed.loss == pytest.approx(original.loss) + np.testing.assert_allclose(changed.gradient, original.gradient[perm]) + np.testing.assert_allclose(changed.curvature, original.curvature[perm]) + + +def test_vector_weight_replication_and_no_split_root(): + x, y = [[0], [1]], np.array([[-2.0, 1.0], [2.0, 3.0]]) + weighted = fit_vector_stump(x, np.zeros_like(y), y, weight=[1, 3]) + ids = [0, 1, 1, 1] + replicated = fit_vector_stump(np.array(x)[ids], np.zeros((4, 2)), y[ids]) + np.testing.assert_allclose(weighted.predict(x), replicated.predict(x)) + root = fit_vector_stump([[0], [0]], np.zeros_like(y), y) + assert root.condition is None + np.testing.assert_allclose(root.predict([[0]]), [[0, 4 / 3]]) + + +@pytest.mark.parametrize("projection", [[[0], [0]], [[1]], [[np.nan], [1]]]) +def test_vector_rejects_invalid_projection(projection): + with pytest.raises(ValueError): + fit_vector_stump([[0], [1]], [[0, 0], [0, 0]], [[1, 2], [3, 4]], projection=projection) + + +def test_lambda_geometry_is_a_frozen_weighted_pair_surrogate(): + raw = np.array([0.1, 0.8, -0.3]) + result = pairwise(raw, [0, 2, 1], [0, 0, 0], lambdas=True) + frozen = {(i, j): factor * 3 for i, j, factor in result.pairs} + # Hold ranking-derived weights fixed for differentiation; no derivative of NDCG. + for i in range(3): + delta = np.eye(3)[i] * 1e-5 + plus = pairwise(raw + delta, [0, 2, 1], [0, 0, 0], pair_weight=frozen) + minus = pairwise(raw - delta, [0, 2, 1], [0, 0, 0], pair_weight=frozen) + assert (plus.loss - minus.loss) / 2e-5 == pytest.approx(result.gradient[i], abs=1e-9) + + +def test_vector_general_projection_affects_only_split_statistics(): + x = [[0, 0], [0, 1], [1, 0], [1, 1]] + y = np.array([[-1.0, -3.0], [-1.0, 3.0], [1.0, -3.0], [1.0, 3.0]]) + tree = fit_vector_stump(x, np.zeros_like(y), y, projection=[[1.0], [1.0]]) + assert tree.condition.feature == 1 + np.testing.assert_allclose(tree.predict(x), [[0, -2], [0, 2], [0, -2], [0, 2]]) + + +def test_ranking_extreme_score_and_zero_weight_query(): + result = pairwise([-1000, 1000], [1, 0], [0, 0]) + assert result.loss == 2000 + np.testing.assert_array_equal(result.gradient, [-1, 1]) + np.testing.assert_array_equal(result.curvature, [0, 0]) + zero = pairwise([-1000, 1000], [1, 0], [0, 0], query_weight={0: 0}) + assert zero.loss == 0 + np.testing.assert_array_equal(zero.gradient, [0, 0]) diff --git a/tests/v1/test_housing_data.py b/tests/v1/test_housing_data.py new file mode 100644 index 0000000..d288aef --- /dev/null +++ b/tests/v1/test_housing_data.py @@ -0,0 +1,84 @@ +"""Independent hand calculations and seeded partition checks for Housing.""" + +import numpy as np +import pytest +from benchmarks.v1.housing import split_indices, transform + + +def test_raw_columns_ratios_and_target_units(): + raw = [[-120, 35, 10, 100, 20, 60, 10, 4, 250000]] + x, y = transform(raw) + np.testing.assert_array_equal(x, [[4, 10, 10, 2, 60, 6, 35, -120]]) + np.testing.assert_array_equal(y, [2.5]) + assert x.dtype == y.dtype == np.dtype(" 0) + + +def test_commit_rejects_broadcastable_row_mismatches(): + from dataclasses import replace + + train, validation, track = start() + p = proposal(track, train, 1, 0) + with pytest.raises(ValueError, match="rows"): + advance(track, p, [[0]], validation, score=lambda r: 0.0) + wrong = replace(p.update, raw_after=(p.update.raw_after[0],)) + with pytest.raises(ValueError, match="shape"): + advance(track, replace(p, update=wrong), train, validation, score=lambda r: 0.0) diff --git a/tests/v1/test_mixed_reference.py b/tests/v1/test_mixed_reference.py new file mode 100644 index 0000000..5cffb78 --- /dev/null +++ b/tests/v1/test_mixed_reference.py @@ -0,0 +1,192 @@ +"""Raw mixed inputs, full-depth trees and vector payload conformance fixtures.""" + +import numpy as np +import pytest + +from .reference.classification import binary +from .reference.mixed import Transformer, grow +from .reference.tree import fit_tree +from .reference.vector import fit_vector_stump + + +def fixture(): + x = [[0, 0], [0, 1], [1, 0], [1, 1]] + y = np.array([[-1.0, -3.0], [-1.0, 3.0], [1.0, -3.0], [1.0, 3.0]]) + transformer = Transformer.fit(x, names=("a", "b"), kinds=("numeric", "numeric"), bins=2) + return x, y, transformer + + +@pytest.mark.parametrize("policy", ["depthwise", "best_first", "symmetric"]) +def test_multilevel_vector_two_rounds_and_row_conservation(policy): + x, y, transformer = fixture() + y[:, 0] *= 2 # Positive summed gain at the second layer despite ridge cost. + raw = np.zeros_like(y) + for iteration in range(2): + tree = grow(x, raw - y, np.ones_like(y), transformer, policy=policy, max_depth=2) + assert len(tree.nodes) == 7 + leaves = [node for node in tree.nodes if node.condition is None] + assert sorted(i for node in leaves for i in node.rows) == list(range(4)) + assert all(len(node.rows) == 1 for node in leaves) + expected = y / 2 if iteration == 0 else 0.95 * y / 2 + np.testing.assert_allclose(tree.predict(x), expected) + raw += 0.1 * tree.predict(x) + np.testing.assert_allclose(raw, 0.0975 * y) + + +@pytest.mark.parametrize("policy", ["depthwise", "best_first", "symmetric"]) +def test_k1_matches_existing_scalar_reference(policy): + x = [[0], [1], [2], [3], [np.nan]] + y = np.array([0.0, -1.0, 3.0, 1.0, 4.0]) + tr = Transformer.fit(x, names=("x",), kinds=("numeric",), bins=4) + tree = grow(x, -y[:, None], np.ones((5, 1)), tr, policy=policy, max_depth=3) + scalar = fit_tree(tr.transform(x), -y, np.ones(5), policy=policy, max_depth=3) + np.testing.assert_allclose(tree.predict(x)[:, 0], scalar.predict(tr.transform(x))) + + +def test_category_middle_value_and_unknown_missing_routing(): + x = [["a"], ["a"], ["m"], ["m"], ["z"], ["z"], [None]] + y = np.array([1, 1, -2, -2, 1, 1, -2.0])[:, None] + tr = Transformer.fit(x, names=("category",), kinds=("categorical",)) + tree = grow(x, -y, np.ones_like(y), tr, max_depth=2) + root = tree.nodes[0] + assert root.condition == (0, 1, True) + np.testing.assert_allclose(tree.predict([["m"], ["unseen"], [None]]), [[-1.5], [-1.5], [-1.5]]) + np.testing.assert_allclose(tree.predict([["a"], ["z"]]), [[0.8], [0.8]]) + + +def test_raw_mixed_binary_two_rounds_train_only_transform(): + x = [[0, "a"], [1, "a"], [0, "b"], [1, "b"], [np.nan, None]] + y = np.array([0, 0, 1, 1, 1]) + tr = Transformer.fit(x, names=("numeric", "category"), kinds=("numeric", "categorical"), bins=2) + raw = np.zeros(5) + test_x = [[100, "new"], [0, "b"], [np.nan, None]] + prediction = np.zeros(3) + trees = [] + for _ in range(2): + _, g, h = binary(raw, y) + tree = grow(x, g[:, None], h[:, None], tr, max_depth=2) + raw += 0.1 * tree.predict(x)[:, 0] + prediction += 0.1 * tree.predict(test_x)[:, 0] + trees.append(tree) + np.testing.assert_allclose(prediction, sum(0.1 * t.predict(test_x)[:, 0] for t in trees)) + assert binary(raw, y)[0] < np.log(2) + assert tr.encoders[1].values == ("a", "b") + assert tr.encoders[0].cuts == (0.5,) + assert prediction[0] == prediction[2] # unknown follows fitted missing branch + + +def test_vector_projection_preserves_payload_and_matches_stump(): + x, y, tr = fixture() + projected = grow(x, -y, np.ones_like(y), tr, max_depth=1, projection=[[1], [0]]) + stump = fit_vector_stump(tr.transform(x), np.zeros_like(y), y, projection=[[1], [0]]) + assert projected.nodes[0].condition[0] == 0 + np.testing.assert_allclose(projected.predict(x), stump.predict(tr.transform(x))) + permuted = grow(x, -y[:, ::-1], np.ones_like(y), tr, max_depth=2) + original = grow(x, -y, np.ones_like(y), tr, max_depth=2) + np.testing.assert_allclose(original.predict(x)[:, ::-1], permuted.predict(x)) + + +def test_weight_replication_and_input_mutation_do_not_change_tree(): + x, y, tr = fixture() + original = grow(x, -y, np.ones_like(y), tr, weight=[1, 2, 1, 2], max_depth=2) + ids = [0, 1, 1, 2, 3, 3] + repeated = grow(np.array(x)[ids], -y[ids], np.ones((6, 2)), tr, max_depth=2) + np.testing.assert_allclose(original.predict(x), repeated.predict(x)) + before = original.predict(x) + y[:] = 100 + np.testing.assert_array_equal(original.predict(x), before) + + +def test_all_missing_or_zero_information_has_no_split(): + for kind in ("numeric", "categorical"): + x = [[None], [None]] + tr = Transformer.fit(x, names=("x",), kinds=(kind,)) + tree = grow(x, [[1], [1]], [[0], [0]], tr) + assert len(tree.nodes) == 1 + np.testing.assert_allclose(tree.predict(x), [[-2], [-2]]) + + +def test_vector_regularization_can_stop_a_split_helpful_to_one_output(): + x, y, tr = fixture() + tree = grow(x, -y, np.ones_like(y), tr, max_depth=2) + assert len(tree.nodes) == 3 + # Second-layer gain: .5 from separating +/-1, minus 1.5 for splitting + # the other channel's constant magnitude 3 into separately penalized leaves. + np.testing.assert_allclose(tree.predict(x), [[0, -2], [0, 2], [0, -2], [0, 2]]) + + +@pytest.mark.parametrize("policy", ["depthwise", "best_first"]) +def test_multilevel_category_numeric_routing_with_exact_unregularized_leaves(policy): + x = [[0, "a"], [1, "a"], [0, "m"], [1, "m"], [0, "z"], [1, "z"]] + y = np.array([[-2, -3], [2, -3], [-2, 0], [2, 0], [-2, 3], [2, 3.0]]) + tr = Transformer.fit(x, names=("number", "category"), kinds=("numeric", "categorical"), bins=2) + tree = grow(x, -y, np.ones_like(y), tr, max_depth=3, policy=policy, reg_lambda=0) + leaves = [node for node in tree.nodes if node.condition is None] + assert len(leaves) == 6 and max(node.depth for node in leaves) == 3 + np.testing.assert_allclose(tree.predict(x), y) + assert sorted(i for node in leaves for i in node.rows) == list(range(6)) + assert {node.condition[0] for node in tree.nodes if node.condition} == {0, 1} + + +@pytest.mark.parametrize("policy", ["depthwise", "best_first", "symmetric"]) +def test_missing_direction_and_zero_weight_rows(policy): + x = [[0], [1], [np.nan], [np.nan]] + tr = Transformer.fit(x, names=("x",), kinds=("numeric",), bins=2) + for target, missing_left in (([-2, 2, -2, 99], True), ([-2, 2, 2, 99], False)): + y = np.array(target, dtype=float)[:, None] + tree = grow(x, -y, np.ones_like(y), tr, weight=[1, 1, 1, 0], max_depth=1, policy=policy) + assert tree.nodes[0].condition[2] is missing_left + assert tree.predict([[np.nan]])[0, 0] == pytest.approx(-4 / 3 if missing_left else 4 / 3) + + +@pytest.mark.parametrize("max_depth", [-1, True, 1.5]) +def test_invalid_growth_depth(max_depth): + x, y, tr = fixture() + with pytest.raises(ValueError): + grow(x, -y, np.ones_like(y), tr, max_depth=max_depth) + + +def test_schema_and_projection_rejection(): + x, y, tr = fixture() + with pytest.raises(ValueError): + Transformer.fit(x, names=("a", "a"), kinds=("numeric", "numeric")) + with pytest.raises(ValueError): + tr.transform([[0]]) + with pytest.raises(ValueError): + grow(x, -y, np.ones_like(y), tr, projection=[[0], [0]]) + with pytest.raises(ValueError): + grow(x, -y, -np.ones_like(y), tr) + + +def test_mixed_pipeline_identity_includes_fitted_transformer_and_raw_content(): + from .reference.runs import bind_identity + + x = [[0, "a"], [1, "b"], [2, "a"]] + tr = Transformer.fit(x, names=("n", "c"), kinds=("numeric", "categorical"), bins=2) + identity = tr.identity([10, 20, 30], x) + assert identity == tr.identity([10, 20, 30], x) + changed_cuts = Transformer.fit(x, names=("n", "c"), kinds=("numeric", "categorical"), bins=3) + assert identity != changed_cuts.identity([10, 20, 30], x) + problem = bind_identity(identity, [10, 20, 30], target=([10, 20, 30], [0, 1, 0])) + assert len(problem) == 64 + with pytest.raises(ValueError): + bind_identity(identity, [30, 20, 10], target=([30, 20, 10], [0, 1, 0])) + + +def test_symmetric_vector_uses_one_shared_condition_per_layer(): + x, y, tr = fixture() + y[:, 0] *= 2 + tree = grow(x, -y, np.ones_like(y), tr, policy="symmetric", max_depth=2) + for depth in (0, 1): + conditions = { + n.condition for n in tree.nodes if n.depth == depth and n.condition is not None + } + assert len(conditions) == 1 + + +def test_nonfinite_combined_gain_rejected(): + x = [[0], [0], [1], [1]] + tr = Transformer.fit(x, names=("x",), kinds=("numeric",), bins=2) + gradient = np.array([[1e154, 1e154]] * 2 + [[-1e154, -1e154]] * 2) + with pytest.raises(ValueError, match="non-finite"): + grow(x, gradient, np.ones((4, 2)), tr) diff --git a/tests/v1/test_parametric_controls.py b/tests/v1/test_parametric_controls.py new file mode 100644 index 0000000..70798e7 --- /dev/null +++ b/tests/v1/test_parametric_controls.py @@ -0,0 +1,38 @@ +import numpy as np +import pytest +from benchmarks.v1.parametric import ( + fit_global_formula, + fit_paid_composition, + predict_global_formula, +) + + +def test_global_formula_recovers_known_curve_and_monotonicity(): + x = np.linspace(0.1, 4, 40) + y = 3 * -np.expm1(-0.7 * x) + saved = fit_global_formula(x, y, weight=np.linspace(0.5, 2, 40)) + np.testing.assert_allclose([saved["amplitude"], saved["rate"]], [3, 0.7], rtol=1e-6) + p = predict_global_formula(saved, x) + np.testing.assert_allclose(p, y, atol=1e-7) + assert np.all(np.diff(p) > 0) + + +@pytest.mark.parametrize( + "counts,totals,index,amount", + [ + ([2, 0], [10, 0], [0], [10]), + ([1, 0], [11, 0], [0], [10]), + ([1, 0], [10, 0], [2], [10]), + ([1, 0], [0, 0], [0], [0]), + ], +) +def test_paid_composition_rejects_mismatched_or_orphan_claims(counts, totals, index, amount): + with pytest.raises(ValueError): + fit_paid_composition(np.ones((2, 2)), counts, totals, [1, 1], index, amount) + + +def test_formula_rejects_invalid_structure_and_budget(): + with pytest.raises(ValueError): + fit_global_formula(np.array([0.0, 1.0]), [1.0, 2.0]) + with pytest.raises(ValueError): + fit_global_formula(np.array([1.0, 2.0]), [1.0, 2.0], max_nfev=0) diff --git a/tests/v1/test_positive_aft_reference.py b/tests/v1/test_positive_aft_reference.py new file mode 100644 index 0000000..bf790c4 --- /dev/null +++ b/tests/v1/test_positive_aft_reference.py @@ -0,0 +1,284 @@ +"""A7-A10 checks against hand calculations and independent calculus.""" + +import math + +import numpy as np +import pytest + +from .reference.positive import ( + gamma, + gamma_base, + poisson, + poisson_base, + poisson_predict, + policy_losses, + tweedie, +) +from .reference.survival import aft, aft_predict, normal_tail +from .reference.tree import fit_tree + + +def test_poisson_exposure_base_and_hand_geometry(): + base = poisson_base([1, 3], [1, 2], weight=[1, 2], minimum_rate=1e-8) + assert base == pytest.approx(math.log(7 / 5)) + loss, g, h = poisson([0, 0], [0, 2], [1, 2]) + assert loss == pytest.approx((3 - math.log(2)) / 2) + np.testing.assert_allclose(g, [1, 0]) + np.testing.assert_allclose(h, [1, 2]) + _, _, doubled = poisson([0, 0], [0, 2], [2, 4]) + np.testing.assert_allclose(doubled, 2 * h) + assert poisson_base([0, 0], [1, 2], minimum_rate=0.01) == pytest.approx(math.log(0.01)) + + +def test_gamma_tweedie_hand_values(): + loss, g, h = gamma([0, 0], [1, 3]) + assert loss == 2 + np.testing.assert_allclose(g, [0, -2]) + np.testing.assert_allclose(h, [1, 3]) + loss, g, h = tweedie([0, 0], [0, 2], power=1.5) + assert loss == 4 + np.testing.assert_allclose(g, [1, -1]) + np.testing.assert_allclose(h, [0.5, 1.5]) + + +@pytest.mark.parametrize("kind", ["poisson", "gamma", "tweedie", "event", "censored"]) +def test_positive_and_aft_finite_differences(kind): + def objective(raw): + if kind == "poisson": + return poisson(raw, [2, 1], [1.5, 0.7]) + if kind == "gamma": + return gamma(raw, [2.0, 0.7]) + if kind == "tweedie": + return tweedie(raw, [0.0, 2.0], power=1.3) + return aft(raw, [1.2, 3.0], [1.2, 3.0] if kind == "event" else [np.inf, np.inf], sigma=0.8) + + raw = np.array([0.3, -0.2]) + _, g, h = objective(raw) + for i in range(2): + delta = np.eye(2)[i] * 1e-5 + plus, minus = objective(raw + delta), objective(raw - delta) + assert (plus[0] - minus[0]) / 2e-5 * 2 == pytest.approx(g[i], abs=1e-8) + assert (plus[1][i] - minus[1][i]) / 2e-5 == pytest.approx(h[i], abs=1e-8) + + +def test_aft_event_censoring_and_units(): + event = aft([0], [1], [1]) + censored = aft([0], [1], [np.inf]) + assert event[0] == pytest.approx(0.5 * math.log(2 * math.pi)) + assert censored[0] == pytest.approx(math.log(2)) + assert event[1][0] == 0 + assert censored[1][0] == pytest.approx(-math.sqrt(2 / math.pi)) + assert censored[2][0] == pytest.approx(2 / math.pi) + output = aft_predict([math.log(2)], sigma=1, times=[1, 2, 4], probabilities=[0.1, 0.5, 0.9]) + assert output["median"][0] == pytest.approx(2) + assert output["mean"][0] == pytest.approx(2 * math.exp(0.5)) + assert output["survival"][0, 1] == pytest.approx(0.5) + assert np.all(np.diff(output["survival"][0]) < 0) + assert np.all(np.diff(output["quantile"][0]) > 0) + assert output["quantile"][0, 1] == pytest.approx(2) + + +@pytest.mark.parametrize("z", [-10.0, 0.0, 8.0, 10.0, 20.0, 30.0, 40.0, 100.0]) +def test_normal_tail_against_independent_integral(z): + logsf, mills, curvature = normal_tail(z) + if z <= 30: + sf = math.erfc(z / math.sqrt(2)) / 2 + assert logsf == pytest.approx(math.log(sf), abs=1e-11) + else: + # SF(z)/phi(z) = integral_0^inf exp(-u-u²/(2z²))du / z. + # Gauss-Laguerre integration is independent of the continued fraction. + nodes, weights = np.polynomial.laguerre.laggauss(64) + ratio = sum(weights * np.exp(-(nodes**2) / (2 * z * z))) / z + assert logsf == pytest.approx( + -z * z / 2 - 0.5 * math.log(2 * math.pi) + math.log(ratio), abs=1e-11 + ) + assert mills == pytest.approx(1 / ratio, rel=1e-12) + assert np.isfinite(logsf) and mills >= 0 and 0 <= curvature <= 1.000001 + if z >= 40: + assert curvature > 0.999 + + +@pytest.mark.parametrize( + "objective,target,extra", + [ + (poisson, [0, 2], {"exposure": [1, 1]}), + (gamma, [1, 3], {}), + (tweedie, [0, 2], {"power": 1.5}), + ], +) +def test_positive_two_round_root_and_integer_weights(objective, target, extra): + raw = np.zeros(2) + for _ in range(2): + _, g, h = objective(raw, target, weight=[1, 3], **extra) + tree = fit_tree([[0], [0]], g, h, weight=[1, 3], max_depth=0) + mu = math.exp(raw[0]) + if objective is poisson: + expected = (6 - 4 * mu) / (1 + 4 * mu) + elif objective is gamma: + expected = (10 / mu - 4) / (1 + 10 / mu) + else: + expected = (6 / math.sqrt(mu) - 4 * math.sqrt(mu)) / ( + 1 + 2 * math.sqrt(mu) + 3 / math.sqrt(mu) + ) + assert tree.predict([[0]])[0] == pytest.approx(expected) + raw += 0.1 * tree.predict([[0], [0]]) + repeated_extra = {"exposure": [1] * 4} if objective is poisson else extra + repeated = objective(raw[[0, 1, 1, 1]], np.array(target)[[0, 1, 1, 1]], **repeated_extra) + assert repeated[0] == pytest.approx(objective(raw, target, weight=[1, 3], **extra)[0]) + + +def test_aft_two_round_mixed_event_censoring(): + raw = np.zeros(2) + for _ in range(2): + loss, g, h = aft(raw, [1, 1], [1, np.inf]) + z = -raw[0] + sf = math.erfc(z / math.sqrt(2)) / 2 + m = math.exp(-z * z / 2) / math.sqrt(2 * math.pi) / sf + expected = (z + m) / (2 + m * (m - z)) + tree = fit_tree([[0], [0]], g, h, max_depth=0) + assert tree.predict([[0]])[0] == pytest.approx(expected) + raw += 0.1 * tree.predict([[0], [0]]) + assert aft(raw, [1, 1], [1, np.inf])[0] < loss + + +def test_policy_join_paid_count_and_annualized_units(): + records, excluded = policy_losses( + [("a", 2, 0.5), ("b", 0, 1.0), ("c", 1, 1.0), ("d", 0, 1.0)], + [("a", 10.0), ("a", 20.0), ("a", 0.0), ("d", 5.0), ("orphan", 8.0)], + ) + assert records == (("a", 0.5, 30.0, 60.0, 2, 15.0), ("b", 1.0, 0.0, 0.0, 0, 0.0)) + assert set(excluded) == { + ("a", "nonpositive_payment"), + ("orphan", "orphan_payment"), + ("c", "count_without_payment"), + ("d", "payment_without_count"), + } + a = records[0] + assert (a[4] / a[1]) * a[5] == a[3] # paid-count rate * mean paid severity + reverse, _ = policy_losses([("b", 0, 1.0), ("a", 2, 0.5)], [("a", 20.0), ("a", 10.0)]) + assert reverse == records + raw = np.log([60.0, 1.0]) + loss = tweedie(raw, [60, 0], weight=[0.5, 1], power=1.5)[0] + assert loss == pytest.approx((0.5 * 4 * math.sqrt(60) + 2) / 1.5) + + +def test_poisson_prediction_requires_exposure_and_gamma_base(): + original = poisson_predict([math.log(3)], [2]) + doubled = poisson_predict([math.log(3)], [4]) + np.testing.assert_allclose(original["rate"], doubled["rate"]) + np.testing.assert_allclose(doubled["count_mean"], 2 * original["count_mean"]) + assert gamma_base([1, 3], weight=[1, 3]) == pytest.approx(math.log(2.5)) + + +@pytest.mark.parametrize( + "lower,upper", + [ + ([0], [np.inf]), + ([-1], [1]), + ([1], [0.5]), + ([1], [2]), + ([1], [np.nan]), + ([np.nan], [np.inf]), + ([np.inf], [np.inf]), + ([1], []), + ], +) +def test_aft_rejects_unsupported_intervals(lower, upper): + with pytest.raises(ValueError): + aft([0], lower, upper) + + +@pytest.mark.parametrize("sigma", [0, -1, np.nan, np.inf, 1e-200, 1e200]) +def test_aft_rejects_invalid_scale(sigma): + with pytest.raises(ValueError): + aft([0], [1], [1], sigma=sigma) + + +@pytest.mark.parametrize("exposure", [[0], [-1], [np.nan], [np.inf], []]) +def test_poisson_rejects_invalid_exposure(exposure): + with pytest.raises(ValueError): + poisson([0], [1], exposure) + + +@pytest.mark.parametrize( + "objective,target,extra", + [ + (poisson, [-1], {"exposure": [1]}), + (poisson, [0.5], {"exposure": [1]}), + (gamma, [0], {}), + (gamma, [-1], {}), + (tweedie, [-1], {}), + (tweedie, [1], {"power": 1}), + (tweedie, [1], {"power": 2}), + ], +) +def test_invalid_positive_target_support(objective, target, extra): + with pytest.raises(ValueError): + objective([0], target, **extra) + + +def test_out_of_range_geometry_rejected_without_clipping(): + with pytest.raises(ValueError): + poisson([1000], [1], [1]) + with pytest.raises(ValueError): + gamma([-1000], [1]) + with pytest.raises(ValueError): + tweedie([2000], [0]) + with pytest.raises(ValueError): + aft_predict([1000]) + + +def test_aft_integer_weights_and_zero_weight_geometry_boundary(): + raw, lo, hi = np.array([0.2, -0.3]), np.array([1.0, 2.0]), np.array([1.0, np.inf]) + loss, g, h = aft(raw, lo, hi, weight=[1, 3]) + plain = aft(raw, lo, hi) + np.testing.assert_array_equal(g, plain[1]) + np.testing.assert_array_equal(h, plain[2]) + ids = [0, 1, 1, 1] + assert loss == pytest.approx(aft(raw[ids], lo[ids], hi[ids])[0]) + assert aft(raw, lo, hi, weight=[1, 0])[0] == pytest.approx(aft(raw[:1], lo[:1], hi[:1])[0]) + + +def test_policy_metadata_validation_and_paid_count_differs_from_claim_count(): + with pytest.raises(ValueError): + policy_losses([("a", 0, 1), ("a", 0, 1)], []) + rows, _ = policy_losses([("a", 3, 2)], [("a", 10), ("a", 20)]) + assert rows[0][4] == 2 # not raw ClaimNb=3 + assert rows[0][3] == (rows[0][4] / 2) * rows[0][5] == 15 + + +def test_censored_curvature_far_tail_and_output_time_rescaling(): + # log(lower)=40, location=0: ordinary survival CDF subtraction would be zero. + result = aft([0], [math.exp(40)], [np.inf]) + assert result[0] > 800 and result[2][0] > 0.999 + factor = 7 + event = aft([0.3], [2], [2]) + scaled_event = aft([0.3 + math.log(factor)], [2 * factor], [2 * factor]) + assert scaled_event[0] == pytest.approx(event[0] + math.log(factor)) # time-density Jacobian + np.testing.assert_allclose(scaled_event[1:], event[1:]) + censored = aft([0.3], [2], [np.inf]) + scaled_censored = aft([0.3 + math.log(factor)], [2 * factor], [np.inf]) + for actual, expected in zip(scaled_censored, censored, strict=True): + np.testing.assert_allclose(actual, expected) + + +def test_gamma_large_but_representable_ratio_and_tail_transition(): + loss, g, h = gamma([math.log(1e308)], [1e308]) + assert loss == pytest.approx(1 + math.log(1e308)) + assert g[0] == pytest.approx(0, abs=1e-13) + assert h[0] == pytest.approx(1) + np.testing.assert_allclose(normal_tail(8), normal_tail(np.nextafter(8.0, np.inf)), rtol=1e-12) + + +@pytest.mark.parametrize("minimum_rate", [0, -1, np.nan, np.inf]) +def test_all_zero_rate_policy_must_be_explicit_and_valid(minimum_rate): + with pytest.raises(ValueError): + poisson_base([0], [1], minimum_rate=minimum_rate) + + +@pytest.mark.parametrize("weight", [[0, 0], [1, -1], [np.nan, 1], [1]]) +def test_aft_and_positive_reject_invalid_weight(weight): + with pytest.raises(ValueError): + aft([0, 0], [1, 1], [1, np.inf], weight=weight) + with pytest.raises(ValueError): + poisson([0, 0], [1, 1], [1, 1], weight=weight) diff --git a/tests/v1/test_process_runner.py b/tests/v1/test_process_runner.py new file mode 100644 index 0000000..27a4bb6 --- /dev/null +++ b/tests/v1/test_process_runner.py @@ -0,0 +1,30 @@ +import sys + +import pytest +from benchmarks.v1.process_runner import execute + + +def test_success_needs_artifacts(tmp_path): + r = execute([sys.executable, "-c", 'print("done")'], tmp_path, timeout_s=2) + assert r["status"] == "error" and r["exit_code"] == 0 + assert "missing" in r["reason"] + + +def test_failure_and_timeout_are_not_passes(tmp_path): + fail = execute( + [sys.executable, "-c", 'raise RuntimeError("broken")'], tmp_path / "fail", timeout_s=2 + ) + assert fail["status"] == "error" and fail["exit_code"] != 0 + timed = execute( + [sys.executable, "-c", "import time; time.sleep(10)"], tmp_path / "timeout", timeout_s=0.1 + ) + assert timed["status"] == "timeout" + assert "broken" in (tmp_path / "fail/worker.log").read_text() + + +def test_fresh_output_and_hashes(tmp_path): + code = 'from pathlib import Path; Path("predictions.npz").write_bytes(b"data"); Path("model.bin").write_bytes(b"model")' + r = execute([sys.executable, "-c", code], tmp_path, timeout_s=2) + assert r["status"] == "pass" and len(r["artifacts"]["model.bin"]) == 64 + with pytest.raises(ValueError, match="empty"): + execute([sys.executable, "-c", code], tmp_path, timeout_s=2) diff --git a/tests/v1/test_public_aft.py b/tests/v1/test_public_aft.py new file mode 100644 index 0000000..38bd9cb --- /dev/null +++ b/tests/v1/test_public_aft.py @@ -0,0 +1,163 @@ +"""Censored AFT geometry, tail quadrature and scale-aware persistence.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.recipes import aft +from openboost.survival import AFTModel, LogNormalAFT, normal_tail +from tests.v1.reference.mixed import Transformer, grow +from tests.v1.reference.survival import aft as reference +from tests.v1.reference.survival import aft_predict +from tests.v1.reference.survival import normal_tail as reference_tail + + +def fixture(): + x = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [0, 1, 2, 3, 4, 5], + ("x", "c"), + ("numeric", "categorical"), + ) + return Problem( + x, + [[1, 1], [2, np.inf], [3, 3], [5, np.inf], [8, 8], [10, np.inf]], + x.row_ids, + target_kind="event_right", + weight=[1, 2, 3, 0, 1, 2], + offset=np.arange(6.0)[:, None] / 10, + ) + + +@pytest.mark.parametrize("z", [-40, -5, 0, 5, 8, 8.01, 12, 40, 1000, 1e8]) +def test_tail_matches_independent_continued_fraction(z): + np.testing.assert_allclose(normal_tail(z), reference_tail(z), rtol=2e-12, atol=1e-14) + + +@pytest.mark.parametrize("sigma", [0.5, 1.0, 2.0]) +def test_three_round_likelihood_and_trees(sigma): + p = fixture() + obj = LogNormalAFT(sigma) + raw = np.full((6, 1), np.average(np.log(p.target[:, 0]) - p.offset[:, 0], weights=p.weight)) + fit = aft(p, p, context=RunContext("aft", 7), sigma=sigma, rounds=3, bins=4) + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + for step in fit.steps: + loss, g, h = reference( + p.with_offset(raw)[:, 0], p.target[:, 0], p.target[:, 1], sigma=sigma, weight=p.weight + ) + np.testing.assert_allclose(step.gradient, g) + np.testing.assert_allclose(step.curvature, h) + np.testing.assert_allclose(step.loss_before, loss) + tree = grow(p.data.values, g[:, None], h[:, None], transform, weight=p.weight) + np.testing.assert_allclose(step.raw_before, raw) + raw += 0.1 * tree.predict(p.data.values) + np.testing.assert_allclose(step.raw_after, raw) + np.testing.assert_allclose(step.loss_after, obj.loss(p, raw)) + assert fit.state.version == 3 + + +def test_censoring_changes_geometry_and_finite_differences(): + p = fixture() + obj = LogNormalAFT(0.7) + raw = np.zeros((6, 1)) + loss, g, h = obj.geometry(p, raw) + event = replace(p, target=np.column_stack((p.target[:, 0], p.target[:, 0]))) + assert event.identity != p.identity + assert obj.loss(event, raw) != loss + assert not np.allclose(obj.geometry(event, raw)[1], g) + eps = 1e-4 + for i in [0, 1, 2]: + delta = np.zeros_like(raw) + delta[i] = eps + plus, minus = obj.loss(p, raw + delta), obj.loss(p, raw - delta) + np.testing.assert_allclose( + (plus - minus) / (2 * eps), g[i] * p.weight[i] / p.weight.sum(), atol=1e-8 + ) + np.testing.assert_allclose( + (plus + minus - 2 * loss) / eps**2, h[i] * p.weight[i] / p.weight.sum(), atol=1e-6 + ) + + +@pytest.mark.parametrize( + "bounds", [[[0, 1]], [[2, 1]], [[1, 2]], [[1, np.nan]], [[np.inf, np.inf]], [[1, -np.inf]]] +) +def test_invalid_or_unsupported_censoring(bounds): + p = fixture() + with pytest.raises(ValueError): + Problem(p.data, np.repeat(bounds, 6, axis=0), p.row_ids, target_kind="event_right") + + +def test_explicit_target_kind_and_ordinary_target_rejection(): + p = fixture() + assert p.raw_width == 1 and np.isposinf(p.target[1, 1]) + with pytest.raises(ValueError): + replace(p, target_kind="numeric") + with pytest.raises(ValueError): + p.target.setflags(write=True) + from openboost.recipes import squared + + with pytest.raises(ValueError): + squared(p, p, context=RunContext("wrong", 1)) + for sigma in [0, -1, np.inf, 1e-300]: + with pytest.raises(ValueError): + LogNormalAFT(sigma) + + +def test_predictions_scale_monotonicity_and_fresh_process(tmp_path): + import json + import subprocess + import sys + + p = fixture() + fit = aft(p, p, context=RunContext("persist-aft", 1), sigma=0.7) + model = AFTModel(fit.state.model, 0.7) + times = [0.5, 2, 10, 100] + probabilities = [0.1, 0.5, 0.9] + raw = model.model.predict(p.data, offset=p.offset)[:, 0] + got = model.predict(p.data, times=times, probabilities=probabilities, offset=p.offset) + expected = aft_predict(raw, sigma=0.7, times=times, probabilities=probabilities) + for key in got: + np.testing.assert_allclose(got[key], expected[key], rtol=1e-12) + assert np.all(np.diff(got["survival"], axis=1) <= 0) + assert np.all(np.diff(got["quantile"], axis=1) > 0) + path = tmp_path / "aft.json" + model.save(path) + assert AFTModel.load(path).identity == model.identity + x = MixedData( + [[1, "unknown"], [None, None]], [10, 11], p.data.feature_names, p.data.feature_kinds + ) + code = """import json,sys +from openboost import MixedData +from openboost.survival import AFTModel +x=MixedData([[1,'unknown'],[None,None]],[10,11],('x','c'),('numeric','categorical')) +m=AFTModel.load(sys.argv[1]) +print(json.dumps({k:v.tolist() for k,v in m.predict(x,times=[1,10],probabilities=[.2,.8], +offset=[[.1],[.2]]).items()})) +""" + output = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + expected = model.predict(x, times=[1, 10], probabilities=[0.2, 0.8], offset=[[0.1], [0.2]]) + for key in expected: + np.testing.assert_array_equal(output[key], expected[key]) + record = model.record() + record["sigma"] = 0 + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + AFTModel.load(path) + + +def test_rejected_update_and_all_censored_finite_initialization(): + p = fixture() + from openboost.tree import depthwise + + def zero(data, fields): + return depthwise(data, fields, max_depth=0, leaf=lambda *_: 0) + + result = aft(p, p, context=RunContext("reject", 1), learner=zero, step="backtracking") + assert result.state.version == 0 + assert all(not s.accepted and s.raw_before is s.raw_after for s in result.steps) + censored = replace(p, target=np.column_stack((p.target[:, 0], np.full(6, np.inf)))) + assert np.isfinite(LogNormalAFT().base(censored)).all() diff --git a/tests/v1/test_public_binary.py b/tests/v1/test_public_binary.py new file mode 100644 index 0000000..1d669b3 --- /dev/null +++ b/tests/v1/test_public_binary.py @@ -0,0 +1,165 @@ +"""Typed binary labels, stable geometry and complete classification inference.""" + +from openboost import ClassSchema + + +def test_training_class_schema(): + schema = ClassSchema.fit(["yes", "no", "yes"]) + assert schema.values == ("no", "yes") + assert schema.encode(["yes", "no"]).tolist() == [[1], [0]] + + +import json +import subprocess +import sys + +import numpy as np +import pytest + +from openboost import MixedData, NumericData, Problem, RunContext +from openboost.artifacts import Model +from openboost.binning import Binning +from openboost.objectives import Binary, Squared +from openboost.recipes import binary +from openboost.runtime import initialize +from tests.v1.reference.classification import ClassMap, binary_base +from tests.v1.reference.classification import binary as ref_binary +from tests.v1.reference.tree import fit_tree + + +def fixture(): + x = NumericData([[0], [1], [2], [3], [4], [np.nan]], np.arange(6), ("x",)) + labels = ["no", "yes", "no", "yes", "yes", "no"] + schema = ClassSchema.fit(labels) + return Problem(x, schema.encode(labels), x.row_ids, weight=[1, 0, 2, 1, 3, 1], classes=schema) + + +def test_three_rounds_match_independent_logistic_reference(): + p = fixture() + result = binary(p, p, context=RunContext("logistic", 7), rounds=3, bins=6) + base = binary_base(p.target[:, 0], weight=p.weight, clip=1e-6) + assert result.state.model.base[0] == pytest.approx(base) + raw = np.full(6, base) + b = Binning.fit(p.data, bins=6).transform(p.data) + bins = np.where(b.missing.T, np.nan, b.codes.T) + for actual in result.steps: + loss, g, h = ref_binary(raw, p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(actual.gradient, g) + np.testing.assert_allclose(actual.curvature, h) + np.testing.assert_allclose(actual.raw_before[:, 0], raw) + tree = fit_tree(bins, g, h, weight=p.weight) + raw = raw + 0.1 * tree.predict(bins) + np.testing.assert_allclose(actual.raw_after[:, 0], raw) + np.testing.assert_allclose( + [actual.loss_before, actual.loss_after], + [loss, ref_binary(raw, p.target[:, 0], weight=p.weight)[0]], + ) + assert result.state.model.classes == p.classes + assert result.steps[-1].loss_after < result.steps[0].loss_before + + +def test_extreme_logits_and_offset_geometry(): + p = fixture() + p = Problem( + p.data, + p.target, + p.row_ids, + weight=p.weight, + classes=p.classes, + offset=np.arange(6)[:, None] / 10, + ) + raw = np.array([[-1000], [1000], [-40], [40], [0], [1.0]]) + got = Binary.geometry(p, raw) + ref = ref_binary(p.with_offset(raw)[:, 0], p.target[:, 0], weight=p.weight) + for a, b in zip(got, ref, strict=True): + np.testing.assert_allclose(a, b) + assert got[1][3] < 0 and got[2][3] > 0 # Tiny tails must not cancel to zero. + model = binary(p, p, context=RunContext("offset", 1), rounds=2).state.model + probability = model.predict_proba(p.data, offset=p.offset) + np.testing.assert_allclose(probability.sum(axis=1), 1) + from openboost.outputs import binary_probabilities + + np.testing.assert_array_equal( + probability, binary_probabilities(model.predict(p.data) + p.offset) + ) + + +def test_class_schema_matches_reference_and_rejects_wrong_roles(): + p = fixture() + ref = ClassMap.fit(["yes", "no", "yes"]) + assert p.classes.values == ref.values + np.testing.assert_array_equal(p.classes.encode(["yes", "no"])[:, 0], ref.encode(["yes", "no"])) + assert p.classes.decode([1, 0]) == ref.decode([1, 0]) + for labels in (["unknown"], [None], [True]): + with pytest.raises(ValueError): + p.classes.encode(labels) + for labels in (["only"], [1, "two"], [None, 1]): + with pytest.raises(ValueError): + ClassSchema.fit(labels) + with pytest.raises(ValueError): + Problem(p.data, [[2]] * 6, p.row_ids, classes=p.classes) + with pytest.raises(ValueError): + Squared.validate(p) + other = Problem(p.data, p.target, p.row_ids, classes=ClassSchema(("a", "b"))) + with pytest.raises(ValueError, match="schemas"): + binary(p, other, context=RunContext("mismatch", 1), rounds=0) + assert other.identity != p.identity + + +def test_fresh_process_classified_mixed_model_and_corrupt_schema(tmp_path): + x = MixedData( + [[0, "a"], [1, "b"], [2, "a"], [3, None]], + [1, 2, 3, 4], + ("x", "c"), + ("numeric", "categorical"), + ) + schema = ClassSchema.fit([10, 20]) + p = Problem(x, schema.encode([10, 20, 10, 20]), x.row_ids, classes=schema) + model = binary(p, p, context=RunContext("persist", 1), rounds=3).state.model + path = tmp_path / "classifier.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import MixedData +from openboost.artifacts import Model +x = MixedData([[0,'a'],[100,'unknown'],[None,None]], [10,11,12], ('x','c'), ('numeric','categorical')) +m = Model.load(sys.argv[1]) +print(json.dumps([m.predict_proba(x).tolist(), m.predict_label(x)])) +""" + output = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + unseen = MixedData( + [[0, "a"], [100, "unknown"], [None, None]], [10, 11, 12], x.feature_names, x.feature_kinds + ) + np.testing.assert_array_equal(output[0], model.predict_proba(unseen)) + assert tuple(output[1]) == model.predict_label(unseen) + for classes in ([20, 10], [10, 10], [10, "20"], [10, 20, 30]): + record = model.record() + record["classes"] = classes + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + Model.load(path) + + +def test_binary_rejection_best_state_and_initialization_contract(): + p = fixture() + from openboost.tree import depthwise + + def zero(data, fields): + return depthwise(data, fields, max_depth=0, leaf=lambda *_: 0) + + result = binary( + p, p, context=RunContext("reject", 1), rounds=2, learner=zero, step="backtracking" + ) + assert result.state.version == 0 and result.state.best_model is result.state.model + assert all(not item.accepted and item.raw_before is item.raw_after for item in result.steps) + valid = Problem(p.data, 1 - p.target, p.row_ids, weight=p.weight, classes=p.classes) + result = binary(p, valid, context=RunContext("best", 1), rounds=3) + assert result.state.best_model is not result.state.model + assert result.state.best_model.classes == p.classes + one = Problem(p.data, np.zeros((6, 1)), p.row_ids, classes=p.classes) + with pytest.raises(ValueError, match="both classes"): + binary(one, p, context=RunContext("one", 1), rounds=0) + with pytest.raises(ValueError): + binary(p, p, context=RunContext("clip", 1), rounds=0, clip=1e-100) + initial = initialize(RunContext("ties", 1), p, p, [0], score=Binary.loss) + assert set(initial.model.predict_label(p.data)) == {p.classes.values[0]} diff --git a/tests/v1/test_public_categorical.py b/tests/v1/test_public_categorical.py new file mode 100644 index 0000000..9efd5e9 --- /dev/null +++ b/tests/v1/test_public_categorical.py @@ -0,0 +1,182 @@ +"""Mixed input, category equality, missing routes and inference conformance.""" + +from openboost import MixedData + + +def test_owned_mixed_input(): + values = [[0, "b"], [1, "a"], [2, None]] + data = MixedData(values, [1, 2, 3], ("x", "c"), ("numeric", "categorical")) + values[0][1] = "changed" + assert data.values[0, 1] == "b" + + +import json +import subprocess +import sys + +import numpy as np +import pytest + +from openboost import NumericData, Problem, RunContext +from openboost.artifacts import Model +from openboost.binning import Binning +from openboost.ops import candidates, choose, histogram, partition +from openboost.recipes import squared +from openboost.stats import newton +from openboost.tree import Tree, best_first, depthwise, symmetric +from tests.v1.reference.data import CategoryMap +from tests.v1.reference.mixed import Transformer, grow + + +def fixture(): + values = [[0, "c"], [1, "a"], [2, "b"], [3, None], [4, "a"], [5, "c"], [np.nan, "b"], [1, "a"]] + data = MixedData(values, np.arange(8), ("x", "c"), ("numeric", "categorical")) + p = Problem(data, np.zeros((8, 1)), data.row_ids, weight=[1, 0, 2, 1, 3, 1, 2, 1]) + return p, np.array([-4, -1, 4, -2, -3, -4, 4, -1.0]) + + +@pytest.mark.parametrize("policy", [depthwise, best_first, symmetric]) +def test_mixed_topology_matches_raw_reference(policy): + p, gradient = fixture() + fitted = Binning.fit(p.data, bins=4) + b = fitted.transform(p.data) + tree = policy(b, newton(p, gradient, np.ones(8)), max_depth=3) + ref_transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + ref = grow( + p.data.values, + gradient[:, None], + np.ones((8, 1)), + ref_transform, + weight=p.weight, + policy=policy.__name__, + max_depth=3, + ) + assert len(tree.value) == len(ref.nodes) + assert any(tree.feature == 1) + for i, node in enumerate(ref.nodes): + assert tree.value[i] == pytest.approx(node.value[0]) + assert (tree.left[i], tree.right[i]) == (node.left, node.right) + if node.condition: + assert (tree.feature[i], tree.threshold[i], tree.missing_left[i]) == node.condition + unseen = MixedData( + [[-100, "a"], [100, "b"], [None, "unseen"], [2, None]], + [90, 91, 92, 93], + p.data.feature_names, + p.data.feature_kinds, + ) + np.testing.assert_allclose(tree.predict(unseen), ref.predict(unseen.values)) + + +@pytest.mark.parametrize("tokens", [["b", "a", None, "c"], [3, 1, None, 2], [None, None]]) +def test_dictionary_matches_independent_map_and_unknown_is_missing(tokens): + data = MixedData([[v] for v in tokens], np.arange(len(tokens)), ("c",), ("categorical",)) + fitted = Binning.fit(data) + ref = CategoryMap.fit(tokens) + assert fitted.categories[0] == ref.values + unseen = MixedData( + [[tokens[0]], [None], ["unknown"]] + if isinstance(tokens[0], str) or tokens[0] is None + else [[tokens[0]], [None], [99]], + [1, 2, 3], + ("c",), + ("categorical",), + ) + b = fitted.transform(unseen) + code, missing = ref.transform(unseen.values[:, 0]) + np.testing.assert_array_equal(b.codes[0], code) + np.testing.assert_array_equal(b.missing[0], missing) + + +@pytest.mark.parametrize("missing_left", [False, True]) +def test_category_equality_routes_both_missing_directions(missing_left): + p, gradient = fixture() + b = Binning.fit(p.data, bins=4).transform(p.data) + option = next( + c + for c in candidates(histogram(b, newton(p, gradient, np.ones(8)))) + if c.feature == 1 and c.threshold == 1 and c.missing_left == missing_left + ) + assert option.kind == "categorical" + left, right = partition(b, None, option) + expected = [2, 3, 6] if missing_left else [2, 6] + assert list(left) == expected + assert sorted(list(left) + list(right)) == list(range(8)) + np.testing.assert_allclose(option.left[0], np.dot(p.weight[left], gradient[left])) + + +def test_categorical_recipe_roundtrip_fresh_process(tmp_path): + p, target = fixture() + p = Problem(p.data, target[:, None], p.row_ids, weight=p.weight) + model = squared(p, p, context=RunContext("categorical", 3), rounds=3, bins=4).state.model + path = tmp_path / "model.json" + model.save(path) + restored = Model.load(path) + assert restored.identity == model.identity + code = """import json, sys +from openboost import MixedData +from openboost.artifacts import Model +x = MixedData([[0, 'a'], [100, 'b'], [None, 'unseen']], [1, 2, 3], ('x','c'), ('numeric','categorical')) +print(json.dumps(Model.load(sys.argv[1]).predict(x).tolist())) +""" + result = subprocess.check_output([sys.executable, "-c", code, str(path)], text=True) + x = MixedData( + [[0, "a"], [100, "b"], [None, "unseen"]], [1, 2, 3], ("x", "c"), ("numeric", "categorical") + ) + np.testing.assert_array_equal(json.loads(result), model.predict(x)) + for _kind, bad in ( + ("duplicate", ["a", "a", "c"]), + ("unsorted", ["c", "b", "a"]), + ("mixed", ["a", 1]), + ("empty", []), + ): + record = model.record() + record["terms"][0]["learner"]["categories"][1] = bad + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + Model.load(path) + + +def test_foreign_kinds_dictionary_and_invalid_tokens_rejected(): + p, gradient = fixture() + b = Binning.fit(p.data).transform(p.data) + tree = depthwise(b, newton(p, gradient, np.ones(8))) + numeric = NumericData(np.zeros((8, 2)), p.row_ids, p.data.feature_names) + with pytest.raises(ValueError): + tree.predict(numeric) + candidate = choose(candidates(histogram(b, newton(p, gradient, np.ones(8))))) + other = Binning( + b.binning.feature_names, b.binning.cuts, (None, ("a", "b", "c", "d")) + ).transform(p.data) + with pytest.raises(ValueError): + partition(other, None, candidate) + for tokens in ([True, False], [1, "a"], [1.5, 2.5]): + with pytest.raises(ValueError): + MixedData([[v] for v in tokens], [1, 2], ("c",), ("categorical",)) + record = tree.record() + record["categories"][1] = None + with pytest.raises(ValueError): + Tree.from_record(record) + + +def test_all_missing_and_missing_only_category_split(tmp_path): + data = MixedData( + [["a", None], ["a", None], [None, None], [None, None]], + [1, 2, 3, 4], + ("c", "empty"), + ("categorical", "categorical"), + ) + p = Problem(data, [[0]] * 4, data.row_ids) + b = Binning.fit(data).transform(data) + options = candidates(histogram(b, newton(p, [-2, -2, 2, 2], [1] * 4))) + assert options and all(c.feature == 0 for c in options) + tree = depthwise(b, newton(p, [-2, -2, 2, 2], [1] * 4)) + path = tmp_path / "tree.json" + tree.save(path) + unseen = MixedData( + [["a", "new"], ["new", None]], [10, 11], data.feature_names, data.feature_kinds + ) + assert Tree.load(path).predict(unseen)[0, 0] != tree.predict(unseen)[1, 0] + with pytest.raises(ValueError): + Binning(("c",), ([],), ("abc",)) diff --git a/tests/v1/test_public_composition.py b/tests/v1/test_public_composition.py new file mode 100644 index 0000000..cffbaf5 --- /dev/null +++ b/tests/v1/test_public_composition.py @@ -0,0 +1,129 @@ +"""Matched paid-loss problems and persisted two-model composition.""" + +import json +import subprocess +import sys + +import numpy as np +import pytest + +from openboost import MixedData, RunContext +from openboost.artifacts import Model +from openboost.composition import FrequencySeverity, paid_loss_problems +from openboost.outputs import poisson_mean, positive_mean +from openboost.recipes import gamma, poisson + + +def fixture(): + data = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"]], + [10, 11, 12, 13, 14], + ("x", "c"), + ("numeric", "categorical"), + ) + count = np.array([0, 2, 1, 3, 0]) + amount = np.array([0, 6, 2, 15, 0]) + exposure = np.array([0.5, 1, 2, 0.5, 1]) + return data, count, amount, exposure + + +def trained(): + data, count, amount, e = fixture() + frequency, severity = paid_loss_problems(data, count, amount, e, weight=[1, 2, 3, 1, 0]) + f = poisson(frequency, frequency, context=RunContext("paid-count", 7), rounds=3) + s = gamma(severity, severity, context=RunContext("paid-severity", 7), rounds=3) + assert f.state.version == s.state.version == 3 + return FrequencySeverity(f.state.model, s.state.model), data, e + + +def test_aggregate_roles_and_weight_units(): + data, count, amount, e = fixture() + f, s = paid_loss_problems(data, count, amount, e, weight=[1, 2, 3, 1, 0]) + np.testing.assert_array_equal(f.target[:, 0], count) + np.testing.assert_array_equal(f.structure["exposure"][:, 0], e) + np.testing.assert_array_equal(s.row_ids, [11, 12, 13]) + np.testing.assert_array_equal(s.target[:, 0], [3, 2, 5]) + np.testing.assert_array_equal(s.weight, [4, 3, 3]) + + +def test_composition_product_exposure_and_policy_order(): + model, data, e = trained() + offset = np.arange(5.0)[:, None] / 10 + got = model.predict(data, data, e, frequency_offset=offset, severity_offset=-offset) + f = poisson_mean(model.frequency.predict(data, offset=offset), e) + s = positive_mean(model.severity.predict(data, offset=-offset)) + np.testing.assert_allclose(got["annualized_mean"], f["rate"] * s) + np.testing.assert_allclose(got["period_mean"], f["count_mean"] * s) + doubled = model.predict(data, data, 2 * e, frequency_offset=offset, severity_offset=-offset) + np.testing.assert_allclose(doubled["period_mean"], 2 * got["period_mean"]) + np.testing.assert_array_equal(doubled["annualized_mean"], got["annualized_mean"]) + order = [4, 2, 1, 3, 0] + other = MixedData( + data.values[order], data.row_ids[order], data.feature_names, data.feature_kinds + ) + reordered = model.predict(other, other, e[order]) + base = model.predict(data, data, e) + for key in base: + np.testing.assert_array_equal(reordered[key], base[key][order]) + with pytest.raises(ValueError, match="row IDs"): + model.predict(data, other, e) + + +@pytest.mark.parametrize( + "counts,totals", + [ + ([0, 2], [1, 3]), + ([1, 0], [0, 0]), + ([0.5, 1], [1, 2]), + ([-1, 1], [1, 2]), + ([0, 0], [0, 0]), + ], +) +def test_bad_aggregates_rejected(counts, totals): + data = MixedData([[1], [2]], [1, 2], ("x",), ("numeric",)) + with pytest.raises(ValueError): + paid_loss_problems(data, counts, totals, [1, 1]) + + +def test_fresh_process_bundle_and_corruption(tmp_path): + model, data, e = trained() + path = tmp_path / "composition.json" + model.save(path) + loaded = FrequencySeverity.load(path) + assert loaded.identity == model.identity + x = MixedData([[1, "unknown"], [None, None]], [20, 21], data.feature_names, data.feature_kinds) + code = """import json, sys +from openboost import MixedData +from openboost.composition import FrequencySeverity +x = MixedData([[1,'unknown'],[None,None]], [20,21], ('x','c'), ('numeric','categorical')) +m = FrequencySeverity.load(sys.argv[1]) +print(json.dumps({k:v.tolist() for k,v in m.predict(x,x,[0.5,2], +frequency_offset=[[0.1],[0.2]], severity_offset=[[0.2],[0.3]]).items()})) +""" + got = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + expected = model.predict( + x, x, [0.5, 2], frequency_offset=[[0.1], [0.2]], severity_offset=[[0.2], [0.3]] + ) + for key in expected: + np.testing.assert_array_equal(got[key], expected[key]) + for field in ("frequency", "severity"): + record = model.record() + record[field]["base"] = [1, 2] + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + FrequencySeverity.load(path) + path.write_text('{"format":"openboost-frequency-severity-v1","format":"duplicate"}') + with pytest.raises(ValueError, match="duplicate"): + FrequencySeverity.load(path) + + +def test_nested_raw_record_validation_and_declared_dependencies(): + model, data, e = trained() + restored = Model.from_record(model.frequency.record()) + assert restored.identity == model.frequency.identity + swapped = FrequencySeverity(model.severity, model.frequency) + assert swapped.identity != model.identity + with pytest.raises(ValueError): + Model.from_record({"format": "old"}) + with pytest.raises(ValueError): + FrequencySeverity(Model(("x",), [0, 0], ()), model.severity) diff --git a/tests/v1/test_public_cpu_state.py b/tests/v1/test_public_cpu_state.py new file mode 100644 index 0000000..c25919c --- /dev/null +++ b/tests/v1/test_public_cpu_state.py @@ -0,0 +1,209 @@ +"""Independent hand cases for initial public ownership/state/inference contracts.""" + +import json +import os +import subprocess +import sys + +import numpy as np +import pytest + +from openboost.artifacts import ConstantTerm, Model +from openboost.data import NumericData, Problem +from openboost.runtime import RunContext, initialize, preview, propose, resolve + + +def problem(): + data = NumericData([[1], [np.nan]], [11, 12], ("x",)) + return Problem(data, [[5], [7]], [11, 12], weight=[1, 3], offset=[[2], [4]]) + + +def score(p, raw): + return np.dot(p.weight, ((p.with_offset(raw) - p.target) ** 2)[:, 0]) / p.weight.sum() + + +def test_owned_inputs_cannot_be_mutated_or_reenabled(): + x = np.array([[1.0], [2.0]]) + y = np.array([[3.0], [4.0]]) + data = NumericData(x, [1, 2], ("x",)) + p = Problem(data, y, [1, 2]) + identity = p.identity + x[:] = 100 + y[:] = 100 + np.testing.assert_array_equal(data.values[:, 0], [1, 2]) + np.testing.assert_array_equal(p.target[:, 0], [3, 4]) + assert p.identity == identity + for a in [data.values, data.row_ids, p.target, p.weight, p.offset]: + with pytest.raises(ValueError): + a.flags.writeable = True + + +def test_identity_includes_order_content_and_roles(): + a = NumericData([[1], [2]], [1, 2], ("x",)) + b = NumericData([[1], [2]], [2, 1], ("x",)) + assert a.identity != b.identity + assert a.identity == NumericData([[1], [2]], [1, 2], ("x",)).identity + assert ( + Problem(a, [[1], [2]], [1, 2]).identity + != Problem(a, [[1], [2]], [1, 2], weight=[1, 2]).identity + ) + with pytest.raises(ValueError, match="row order"): + Problem(a, [[1], [2]], [2, 1]) + + +def test_rejection_commit_best_and_offset_once(): + p = problem() + initial = initialize(RunContext("one", 3), p, p, [0], score=score) + first = propose(initial, [6], coefficient=0.5) + np.testing.assert_array_equal( + preview(initial, first).predict(p.data, offset=p.offset), [[5], [7]] + ) + assert ( + resolve(initial, first, accept=False, score=lambda *_: pytest.fail("rejection scored")) + is initial + ) + accepted = resolve(initial, first, accept=True, score=score) + assert accepted.best_score == 0 and accepted.version == 1 + np.testing.assert_array_equal(initial.train_raw, [[0], [0]]) + np.testing.assert_array_equal(accepted.train_raw, [[3], [3]]) + worse = resolve(accepted, propose(accepted, [2]), accept=True, score=score) + np.testing.assert_array_equal(worse.train_raw, [[5], [5]]) + np.testing.assert_array_equal(p.with_offset(worse.train_raw), [[7], [9]]) + assert worse.best_model is accepted.model and worse.best_score == 0 + assert score(p, worse.validation_raw) == 4 # original weights applied once + with pytest.raises(ValueError, match="stale"): + resolve(accepted, first, accept=True, score=score) + + +def test_foreign_and_divergent_proposals_and_keyed_rng(): + p = problem() + a = initialize(RunContext("a", 3), p, p, [0], score=score) + b = initialize(RunContext("b", 3), p, p, [0], score=score) + proposal = propose(a, [1]) + with pytest.raises(ValueError, match="foreign"): + resolve(b, proposal, accept=True, score=score) + x = resolve(a, proposal, accept=True, score=score) + y = resolve(a, propose(a, [2]), accept=True, score=score) + with pytest.raises(ValueError, match="foreign"): + resolve(y, propose(x, [1]), accept=True, score=score) + expected = a.context.rng(1, "rows", "sample").integers(1000, size=8) + resolve(a, proposal, accept=False, score=score) + np.testing.assert_array_equal( + expected, a.context.rng(1, "rows", "sample").integers(1000, size=8) + ) + assert not np.array_equal(expected, b.context.rng(1, "rows", "sample").integers(1000, size=8)) + + +def test_invalid_score_is_atomic(): + p = problem() + a = initialize(RunContext("a", 1), p, p, [0], score=score) + with pytest.raises(ValueError, match="finite"): + resolve(a, propose(a, [2]), accept=True, score=lambda *_: np.nan) + assert a.version == 0 and not a.model.terms + with pytest.raises(ValueError): + propose(a, [1, 2]) + + +def test_fresh_process_inference_and_artifact_corruption(tmp_path): + model = Model(("x",), [1, 2], (ConstantTerm([2, 4], 0.5),)) + path = tmp_path / "model.json" + model.save(path) + code = """import sys, numpy as np +from openboost.artifacts import Model +from openboost.data import NumericData +m=Model.load(sys.argv[1]) +x=NumericData([[float('nan')],[3]],[11,12],('x',)) +np.testing.assert_array_equal(m.predict(x,offset=[[1,2],[3,4]]),[[3,6],[5,8]]) +""" + env = os.environ.copy() + env.pop("PYTHONPATH", None) + subprocess.run([sys.executable, "-c", code, str(path)], check=True, env=env, timeout=30) + raw = json.loads(path.read_text()) + raw["terms"][0]["value"] = [1] + path.write_text(json.dumps(raw)) + with pytest.raises(ValueError): + Model.load(path) + raw["format"] = "unknown" + path.write_text(json.dumps(raw)) + with pytest.raises(ValueError, match="schema"): + Model.load(path) + + +@pytest.mark.parametrize("kwargs", [dict(device="cuda"), dict(seed=-1), dict(run_id="")]) +def test_explicit_runtime_rejection(kwargs): + with pytest.raises(ValueError): + RunContext(**(dict(run_id="a", seed=1) | kwargs)) + + +def test_weight_semantics_and_separate_raw_caches(): + p = problem() + assert score(p, np.array([[0.0], [1.0]])) == 5.25 + valid = Problem(NumericData([[9]], [99], ("x",)), [[3]], [99]) + state = initialize(RunContext("two-datasets", 7), p, valid, [1], score=score) + next_state = resolve(state, propose(state, [2]), accept=True, score=score) + np.testing.assert_array_equal(next_state.train_raw, [[3], [3]]) + np.testing.assert_array_equal(next_state.validation_raw, [[3]]) + assert not np.shares_memory(next_state.train_raw, next_state.validation_raw) + assert next_state.best_score == 0 + + +def test_vector_proposal_is_atomic_and_owned(): + p = Problem(NumericData([[1]], [1], ("x",)), [[2, 4]], [1]) + + def metric(p, raw): + return float(np.sum((raw - p.target) ** 2)) + + state = initialize(RunContext("vector", 7), p, p, [0, 0], score=metric) + values = np.array([2.0, 4.0]) + proposal = propose(state, values) + values[:] = 100 + result = resolve(state, proposal, accept=True, score=metric) + np.testing.assert_array_equal(result.train_raw, [[2, 4]]) + assert result.version == 1 and result.best_score == 0 + with pytest.raises(ValueError): + propose(result, [0, np.nan]) + np.testing.assert_array_equal(result.train_raw, [[2, 4]]) + + +@pytest.mark.parametrize("change", ["unknown", "duplicate", "nan", "wrong_width"]) +def test_corrupt_artifacts_fail_closed(tmp_path, change): + model = Model(("x",), [0], (ConstantTerm([1]),)) + path = tmp_path / "m.json" + model.save(path) + record = json.loads(path.read_text()) + if change == "unknown": + record["hidden"] = 1 + elif change == "nan": + record["base"] = [float("nan")] + elif change == "wrong_width": + record["terms"][0]["value"] = [1, 2] + path.write_text(json.dumps(record)) + if change == "duplicate": + path.write_text(path.read_text().replace('"format":', '"base": [1], "format":')) + with pytest.raises(ValueError): + Model.load(path) + + +@pytest.mark.parametrize( + "kwargs", + [ + dict(weight=[0, 0]), + dict(weight=[1, -1]), + dict(offset=[1, 2]), + dict(row_ids=[11.0, 12.0]), + dict(target=[[np.nan], [1]]), + ], +) +def test_problem_rejects_invalid_roles(kwargs): + p = problem() + args = dict(data=p.data, target=[[1], [2]], row_ids=[11, 12]) | kwargs + with pytest.raises(ValueError): + Problem(**args) + + +def test_keyed_stream_does_not_consume_global_rng(): + before = np.random.get_state() + RunContext("isolated", 3).rng(0, "tree", "rows").normal(size=10) + after = np.random.get_state() + assert before[0] == after[0] and before[2:] == after[2:] + np.testing.assert_array_equal(before[1], after[1]) diff --git a/tests/v1/test_public_expectile.py b/tests/v1/test_public_expectile.py new file mode 100644 index 0000000..af86266 --- /dev/null +++ b/tests/v1/test_public_expectile.py @@ -0,0 +1,99 @@ +"""D1 development objective against independent mathematical references.""" + +import numpy as np + +from openboost import NumericData, Problem +from tests.v1.reference.author import expectile_base +from tests.v1.test_public_extensions import ROOT, load + + +def extension(): + return load(ROOT / "expectile/src/ob_expectile/__init__.py", "expectile_extension") + + +def test_weighted_base_matches_stationary_intervals(): + plugin = extension() + data = NumericData(np.arange(5)[:, None], np.arange(5), ("x",)) + p = Problem( + data, + [[-3], [0], [2], [2], [90]], + data.row_ids, + weight=[2, 1, 3, 1, 0], + offset=np.ones((5, 1)), + ) + np.testing.assert_allclose( + plugin.Expectile().base(p), + [expectile_base(p.target[:, 0] - 1, weight=p.weight)], + atol=1e-12, + ) + + +def test_two_round_exhaustive_reference(tmp_path): + import json + + from examples.v1_extensions.expectile_checks import check + from examples.v1_extensions.expectile_oracle import evidence + + (tmp_path / "expectile-expected.json").write_text(json.dumps(evidence())) + check(extension(), tmp_path) + + +def test_derivatives_sign_zero_weight_and_finite_difference(): + from tests.v1.reference.author import expectile + + plugin = extension() + data = NumericData(np.arange(4)[:, None], np.arange(4), ("x",)) + p = Problem(data, [[-2], [0], [3], [5]], data.row_ids, weight=[2, 1, 3, 0]) + raw = np.zeros((4, 1)) + loss, g, h = plugin.Expectile().geometry(p, raw) + expected = expectile(raw[:, 0], p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(loss, expected[0]) + np.testing.assert_allclose(g, expected[1]) + np.testing.assert_allclose(h, expected[2]) + assert g[1] == 0 and h[1] == 1.6 + for i in (0, 2, 3): + delta = np.zeros_like(raw) + delta[i] = 1e-5 + plus = plugin.Expectile().loss(p, raw + delta) + minus = plugin.Expectile().loss(p, raw - delta) + np.testing.assert_allclose( + (plus - minus) / 2e-5, g[i] * p.weight[i] / p.weight.sum(), atol=1e-9 + ) + + +def test_invalid_options_and_zero_rounds(): + import pytest + + from openboost import RunContext + + plugin = extension() + data = NumericData([[0], [1]], [0, 1], ("x",)) + p = Problem(data, [[1], [1]], data.row_ids) + for tau in (0, 1, float("nan"), True, "0.8"): + with pytest.raises(ValueError): + plugin.Expectile(tau) + for rate in (0, -1, float("inf"), True): + with pytest.raises(ValueError): + plugin.fit(p, p, context=RunContext("bad", 0), learning_rate=rate) + result = plugin.fit(p, p, context=RunContext("zero", 0), rounds=0) + assert result.steps == () and result.stop.reason == "budget" + np.testing.assert_array_equal(result.state.train_raw, [[1], [1]]) + + +def test_initialization_across_weighted_asymmetric_fixtures(): + rng = np.random.default_rng(43) + plugin = extension() + data = NumericData(np.arange(12)[:, None], np.arange(12), ("x",)) + for tau in (0.05, 0.5, 0.8, 0.95): + for _ in range(8): + y = rng.integers(-10, 11, 12).astype(float) + weight = rng.integers(0, 5, 12).astype(float) + weight[0] = 1 + p = Problem(data, y[:, None], data.row_ids, weight=weight) + objective = plugin.Expectile(tau) + base = objective.base(p) + np.testing.assert_allclose( + base, [expectile_base(y, tau=tau, weight=weight)], atol=1e-12 + ) + _, gradient, _ = objective.geometry(p, np.broadcast_to(base, (12, 1))) + assert abs(np.dot(weight, gradient)) < 1e-10 diff --git a/tests/v1/test_public_extensions.py b/tests/v1/test_public_extensions.py new file mode 100644 index 0000000..ef350c8 --- /dev/null +++ b/tests/v1/test_public_extensions.py @@ -0,0 +1,29 @@ +"""Source development checks; installed-wheel proof uses the separate verifier.""" + +import ast +import importlib.util +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] / "examples/v1_extensions" + + +def load(path, name): + spec = importlib.util.spec_from_file_location(name, path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def test_development_extension_oracles(tmp_path): + cohort = load(ROOT / "cohort_splits/src/ob_cohort_splits/__init__.py", "cohort") + leaves = load(ROOT / "penalized_leaves/src/ob_penalized_leaves/__init__.py", "leaves") + checks = load(ROOT / "checks.py", "checks") + checks.run_checks(cohort, leaves, tmp_path) + + +def test_extensions_use_only_public_openboost_imports(): + for path in ROOT.glob("*/src/*/*.py"): + for node in ast.walk(ast.parse(path.read_text())): + if isinstance(node, ast.ImportFrom) and (node.module or "").startswith("openboost"): + assert not any(p.startswith("_") for p in node.module.split(".")) + assert not any(a.name.startswith("_") for a in node.names) diff --git a/tests/v1/test_public_formula_runs.py b/tests/v1/test_public_formula_runs.py new file mode 100644 index 0000000..b5fcdb4 --- /dev/null +++ b/tests/v1/test_public_formula_runs.py @@ -0,0 +1,210 @@ +"""Formula geometry and heterogeneous execution against independent references.""" + +import numpy as np + +from openboost import NumericData, Problem + + +def test_structure_is_separate_owned_and_identity_bound(): + x = NumericData([[0], [1]], [1, 2], ("feature",)) + role = np.array([[1.0], [2.0]]) + p = Problem(x, [[1], [2]], x.row_ids, raw_width=2, structure={"x": role}) + role[:] = 99 + np.testing.assert_array_equal(p.structure["x"], [[1], [2]]) + assert p.data.values.shape == (2, 1) + + +import json +import subprocess +import sys +from functools import partial + +import pytest + +from openboost import RunContext +from openboost.artifacts import Model +from openboost.binning import Binning +from openboost.objectives import Formula, Normal, Squared, full_direction +from openboost.recipes import formula, normal, squared +from openboost.runs import RunSpec, run_many +from tests.v1.reference.coupled import directions, formula_base, formula_predict, step +from tests.v1.reference.coupled import formula as ref_formula + + +def fixture(): + x = NumericData([[0], [1], [2], [3], [np.nan], [5]], np.arange(6), ("feature",)) + return Problem( + x, + [[0.5], [1], [2], [2.5], [3], [4]], + x.row_ids, + raw_width=2, + weight=[1, 0, 2, 1, 3, 1], + structure={"x": np.linspace(0.2, 2, 6)[:, None]}, + ) + + +def test_three_rounds_and_full_geometry_match_independent_reference(): + p = fixture() + result = formula(p, p, context=RunContext("formula", 7), rounds=3, bins=6) + base = formula_base(p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(result.state.model.base, base) + raw = np.broadcast_to(base, (6, 2)).copy() + b = Binning.fit(p.data, bins=6).transform(p.data) + bins = np.where(b.missing.T, np.nan, b.codes.T) + objective = partial(ref_formula, x=p.structure["x"][:, 0]) + for actual in result.steps: + loss, gradient, metric = objective(raw, p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(actual.gradient, gradient) + np.testing.assert_allclose(actual.metric, metric) + np.testing.assert_allclose(actual.direction, directions(gradient, metric, damping=0.1)) + ref = step( + bins, + raw, + p.target[:, 0], + objective, + weight=p.weight, + damping=0.1, + rates=tuple(0.1 * 0.5**j for j in range(6)), + ) + assert actual.accepted == ref.accepted + assert actual.coefficients == tuple(t[0] for t in ref.trials) + np.testing.assert_allclose(actual.raw_before, raw) + np.testing.assert_allclose(actual.raw_after, ref.raw_after) + np.testing.assert_allclose([actual.loss_before, actual.loss_after], [loss, ref.loss_after]) + raw = np.asarray(ref.raw_after) + np.testing.assert_allclose( + Formula.predict(raw, p.structure["x"])[:, 0], + formula_predict(raw, p.structure["x"][:, 0])[0], + ) + assert result.steps[-1].loss_after < result.steps[0].loss_before + + +def test_structure_offset_identity_and_rank_deficiency(): + p = fixture() + shifted = Problem( + p.data, + p.target, + p.row_ids, + raw_width=2, + weight=p.weight, + offset=np.full((6, 2), 0.2), + structure=p.structure, + ) + raw = np.broadcast_to(Formula.base(p), (6, 2)) + expected = ref_formula(raw + 0.2, p.target[:, 0], p.structure["x"][:, 0], weight=p.weight) + got = Formula.geometry(shifted, raw) + for actual, ref in zip(got, expected, strict=True): + np.testing.assert_allclose(actual, ref) + assert p.identity != shifted.identity + with pytest.raises(ValueError, match="positive definite"): + full_direction(got[1], got[2], damping=0) + for objective in (Squared, Normal): + with pytest.raises(ValueError): + objective.validate(p) + with pytest.raises(TypeError): + p.structure["extra"] = np.ones((6, 1)) + with pytest.raises(ValueError): + p.structure["x"].flags.writeable = True + changed = Problem( + p.data, + p.target, + p.row_ids, + raw_width=2, + weight=p.weight, + structure={"x": p.structure["x"] + 1}, + ) + assert changed.identity != p.identity and changed.data.identity == p.data.identity + + +def test_formula_roundtrip_requires_explicit_inference_structure(tmp_path): + p = fixture() + model = formula(p, p, context=RunContext("persist", 1), rounds=2).state.model + path = tmp_path / "formula.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import NumericData +from openboost.artifacts import Model +from openboost.objectives import Formula +x = NumericData([[-10], [10], [float('nan')]], [10, 11, 12], ('feature',)) +print(json.dumps(Formula.predict(Model.load(sys.argv[1]).predict(x), [[0.1], [1], [5]]).tolist())) +""" + output = subprocess.check_output([sys.executable, "-c", code, str(path)], text=True) + x = NumericData([[-10], [10], [np.nan]], [10, 11, 12], ("feature",)) + np.testing.assert_allclose( + json.loads(output), Formula.predict(model.predict(x), [[0.1], [1], [5]]) + ) + + +@pytest.mark.parametrize("count", [1, 2, 8]) +def test_heterogeneous_runs_match_independent_and_reordered_execution(count): + p = fixture() + scalar = Problem(p.data, p.target, p.row_ids, weight=p.weight) + distribution = Problem(p.data, p.target, p.row_ids, weight=p.weight, raw_width=2) + jobs = [(squared, scalar), (normal, distribution), (formula, p)] + specs = tuple( + RunSpec( + RunContext(f"run-{i}", 9), + jobs[i % 3][1], + jobs[i % 3][1], + jobs[i % 3][0], + {"rounds": i % 3, "bins": 6}, + ) + for i in range(count) + ) + results = run_many(specs) + reversed_results = {r.run_id: r for r in run_many(reversed(specs))} + for spec, outcome in zip(specs, results, strict=True): + independent = spec.recipe(spec.train, spec.validation, context=spec.context, **spec.options) + assert outcome.error_type is None + assert outcome.result.state.identity == independent.state.identity + assert ( + outcome.result.state.identity == reversed_results[outcome.run_id].result.state.identity + ) + assert len(outcome.result.steps) == spec.options["rounds"] + assert outcome.result.state.train_raw.shape[1] == spec.train.raw_width + for other in results: + if other is not outcome: + assert not np.shares_memory( + outcome.result.state.train_raw, other.result.state.train_raw + ) + + +def test_failed_run_continues_and_duplicate_ids_fail_before_execution(): + p = fixture() + calls = [] + + def fail(*args, **kwargs): + calls.append(1) + raise RuntimeError("injected failure") + + specs = ( + RunSpec(RunContext("failed", 1), p, p, fail), + RunSpec(RunContext("good", 1), p, p, formula, {"rounds": 1}), + ) + outcomes = run_many(specs) + assert outcomes[0].result is None and outcomes[0].error_type == "RuntimeError" + assert outcomes[1].result.state.version == 1 + with pytest.raises(ValueError, match="unique"): + run_many((specs[0], specs[0])) + assert len(calls) == 1 + with pytest.raises(ValueError): + run_many(specs, execution="fused") + foreign = RunSpec(RunContext("foreign", 2), p, p, lambda *a, **kw: outcomes[1].result) + assert run_many([foreign])[0].error_type == "ValueError" + + +def test_invalid_structure_and_options_fail(): + p = fixture() + with pytest.raises(ValueError): + Problem(p.data, p.target, p.row_ids, structure={"x": [[1]]}) + for structure in ({}, {"x": np.zeros((6, 1))}, {"wrong": np.ones((6, 1))}): + bad = Problem(p.data, p.target, p.row_ids, raw_width=2, structure=structure) + with pytest.raises(ValueError): + formula(bad, bad, context=RunContext("invalid", 1), rounds=0) + with pytest.raises(ValueError): + RunSpec(RunContext("invalid", 1), p, p, formula, {"context": "override"}) + options = {"rounds": 0} + spec = RunSpec(RunContext("owned", 1), p, p, formula, options) + options["rounds"] = 100 + assert spec.options["rounds"] == 0 diff --git a/tests/v1/test_public_gamma.py b/tests/v1/test_public_gamma.py new file mode 100644 index 0000000..4acd4d2 --- /dev/null +++ b/tests/v1/test_public_gamma.py @@ -0,0 +1,148 @@ +"""Positive Gamma mean regression against independent formulas and tree growth.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.objectives import Gamma +from openboost.outputs import positive_mean +from openboost.recipes import gamma +from tests.v1.reference.mixed import Transformer, grow +from tests.v1.reference.positive import gamma as reference +from tests.v1.reference.positive import gamma_base + + +def fixture(): + x = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [0, 1, 2, 3, 4, 5], + ("x", "c"), + ("numeric", "categorical"), + ) + return Problem( + x, + [[0.2], [1], [4], [2], [3], [8]], + x.row_ids, + weight=[1, 2, 3, 0, 2, 1], + offset=np.arange(6.0)[:, None] / 10, + ) + + +def test_geometry_base_and_derivatives(): + p = fixture() + base = Gamma.base(p)[0] + np.testing.assert_allclose( + base, gamma_base(p.target[:, 0] * np.exp(-p.offset[:, 0]), weight=p.weight) + ) + raw = np.full((6, 1), base) + got = Gamma.geometry(p, raw) + expected = reference(p.with_offset(raw)[:, 0], p.target[:, 0], weight=p.weight) + for a, b in zip(got, expected, strict=True): + np.testing.assert_allclose(a, b) + np.testing.assert_allclose(np.dot(p.weight, got[1]), 0, atol=1e-12) + eps = 1e-4 + for i in [0, 1, 2]: + delta = np.zeros_like(raw) + delta[i] = eps + plus, minus = Gamma.loss(p, raw + delta), Gamma.loss(p, raw - delta) + np.testing.assert_allclose( + (plus - minus) / (2 * eps), got[1][i] * p.weight[i] / p.weight.sum(), atol=1e-8 + ) + np.testing.assert_allclose( + (plus + minus - 2 * got[0]) / eps**2, + got[2][i] * p.weight[i] / p.weight.sum(), + atol=1e-6, + ) + + +def test_three_rounds_match_independent_tree(): + p = fixture() + fit = gamma(p, p, context=RunContext("gamma", 7), rounds=3, bins=4) + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + raw = np.full((6, 1), gamma_base(p.target[:, 0] * np.exp(-p.offset[:, 0]), weight=p.weight)) + for step in fit.steps: + loss, g, h = reference(p.with_offset(raw)[:, 0], p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(step.gradient, g) + np.testing.assert_allclose(step.curvature, h) + np.testing.assert_allclose(step.loss_before, loss) + tree = grow(p.data.values, g[:, None], h[:, None], transform, weight=p.weight) + np.testing.assert_allclose(step.raw_before, raw) + raw += 0.1 * tree.predict(p.data.values) + np.testing.assert_allclose(step.raw_after, raw) + assert fit.state.version == 3 + + +def test_claim_average_with_count_weights_matches_claim_level_geometry(): + # Equal predictors within each policy: aggregate claim geometry exactly. + x = MixedData([[0, "a"], [1, "b"]], [0, 1], ("x", "c"), ("numeric", "categorical")) + p = Problem(x, [[3], [5]], x.row_ids, weight=[2, 3]) + raw = np.array([[0.2], [0.7]]) + loss, g, h = Gamma.geometry(p, raw) + expected = reference([0.2, 0.2, 0.7, 0.7, 0.7], [2, 4, 3, 5, 7]) + np.testing.assert_allclose(loss, expected[0]) + for got, claim in zip((g, h), expected[1:], strict=True): + np.testing.assert_allclose(got * p.weight, [sum(claim[:2]), sum(claim[2:])]) + # Unit policy weights deliberately produce a different aggregate objective. + assert Gamma.loss(replace(p, weight=[1, 1]), raw) != loss + + +@pytest.mark.parametrize("kind", ["zero", "negative", "structure", "infinite_raw"]) +def test_invalid_inputs_fail(kind): + p = fixture() + if kind in ("zero", "negative"): + p = replace(p, target=np.full((6, 1), 0 if kind == "zero" else -1)) + elif kind == "structure": + p = replace(p, structure={"exposure": np.ones((6, 1))}) + if kind == "infinite_raw": + with pytest.raises((ValueError, FloatingPointError)): + Gamma.geometry(p, np.full((6, 1), -1000)) + else: + with pytest.raises(ValueError): + gamma(p, p, context=RunContext("invalid", 1)) + + +def test_backtracking_rejection_and_positive_output_support(): + from openboost.tree import depthwise + + p = fixture() + + def zero(data, fields): + return depthwise(data, fields, max_depth=0, leaf=lambda *_: 0) + + fit = gamma(p, p, context=RunContext("reject", 1), learner=zero, step="backtracking", rounds=2) + assert fit.state.version == 0 + assert all(not s.accepted and s.raw_before is s.raw_after for s in fit.steps) + for raw in ([[1000]], [[-1000]], [[1, 2]]): + with pytest.raises((ValueError, FloatingPointError)): + positive_mean(raw) + + +def test_fresh_process_mean_roundtrip(tmp_path): + import json + import subprocess + import sys + + from openboost.artifacts import Model + + p = fixture() + model = gamma(p, p, context=RunContext("persist", 1)).state.model + path = tmp_path / "model.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import MixedData +from openboost.artifacts import Model +from openboost.outputs import positive_mean +x = MixedData([[1,'unknown'], [None,None]], [10,11], ('x','c'), ('numeric','categorical')) +print(json.dumps(positive_mean(Model.load(sys.argv[1]).predict(x, offset=[[0.1],[0.2]])).tolist())) +""" + x = MixedData( + [[1, "unknown"], [None, None]], [10, 11], p.data.feature_names, p.data.feature_kinds + ) + expected = positive_mean(model.predict(x, offset=[[0.1], [0.2]])) + got = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + np.testing.assert_array_equal(got, expected) diff --git a/tests/v1/test_public_growth.py b/tests/v1/test_public_growth.py new file mode 100644 index 0000000..20d7189 --- /dev/null +++ b/tests/v1/test_public_growth.py @@ -0,0 +1,153 @@ +"""Independent topology checks for all public numeric growth policies.""" + +import numpy as np +import pytest + +from openboost import NumericData, Problem +from openboost.binning import Binning +from openboost.stats import newton +from openboost.tree import best_first, depthwise, symmetric +from tests.v1.reference.tree import fit_tree + + +@pytest.mark.parametrize("grow", [depthwise, best_first, symmetric]) +@pytest.mark.parametrize("depth,leaves", [(0, 1), (1, 4), (2, 3), (3, 5), (3, 8)]) +def test_policy_matches_independent_reference(grow, depth, leaves): + rng = np.random.default_rng(19) + values = rng.integers(0, 4, (20, 3)).astype(float) + values[::4, 1] = np.nan + x = NumericData(values, np.arange(20), ("a", "b", "c")) + p = Problem(x, np.zeros((20, 1)), x.row_ids, weight=rng.integers(0, 4, 20)) + g, h = rng.normal(size=20), rng.uniform(0.5, 2, 20) + b = Binning.fit(x, bins=4).transform(x) + tree = grow(b, newton(p, g, h), max_depth=depth, max_leaves=leaves) + bins = np.where(b.missing.T, np.nan, b.codes.T) + ref = fit_tree( + bins, g, h, weight=p.weight, policy=grow.__name__, max_depth=depth, max_leaves=leaves + ) + assert len(tree.value) == len(ref.nodes) + for i, node in enumerate(ref.nodes): + assert tree.value[i] == pytest.approx(node.value) + assert (tree.left[i], tree.right[i]) == (node.left, node.right) + if node.condition: + assert (tree.feature[i], tree.threshold[i], tree.missing_left[i]) == ( + node.condition.feature, + node.condition.threshold, + node.condition.missing_left, + ) + else: + assert tree.feature[i] == -1 + np.testing.assert_allclose(tree.predict(x)[:, 0], ref.predict(bins)) + + +def test_symmetric_uses_common_gain_not_individual_winners(): + x = NumericData([[0, 0], [0, 1], [1, 0], [1, 1]], [1, 2, 3, 4], ("a", "b")) + p = Problem(x, [[0]] * 4, x.row_ids) + b = Binning.fit(x, bins=2).transform(x) + fields = newton(p, [-4, -4, 1, 3], [1] * 4) + seen = set() + + def scoring(c): + key = (c.rows_identity, c.key) + assert key not in seen + seen.add(key) + if c.left_count + c.right_count == 4: + return 10 if c.feature == 0 else 0 + return (-1 if c.parent[0] < 0 else 3) if c.feature == 1 else 0 + + tree = symmetric(b, fields, max_depth=2, scoring=scoring) + assert tree.feature.tolist() == [0, 1, 1, -1, -1, -1, -1] + assert tree.threshold[1] == tree.threshold[2] + assert tree.missing_left[1] == tree.missing_left[2] + seen.clear() + limited = symmetric(b, fields, max_depth=2, max_leaves=3, scoring=scoring) + assert len(limited.value) == 3 # Cannot partially split a symmetric layer. + + +@pytest.mark.parametrize("grow", [best_first, symmetric]) +def test_callbacks_leaf_values_and_scores_are_used_once(grow): + from openboost.ops import feasible, newton_leaf, score + + rng = np.random.default_rng(23) + x = NumericData(rng.normal(size=(24, 2)), np.arange(24), ("a", "b")) + p = Problem(x, np.zeros((24, 1)), x.row_ids) + b = Binning.fit(x, bins=6).transform(x) + fields = newton(p, rng.normal(size=24), np.ones(24)) + seen, leaf_calls = set(), [] + + def scoring(c): + key = (c.rows_identity, c.key) + assert key not in seen + seen.add(key) + return score(c) + + def leaf(total, names): + leaf_calls.append(1) + return 2 * newton_leaf(total, names) + + def legality(c): + return c.feature == 1 and feasible(c) + + tree = grow(b, fields, max_depth=4, max_leaves=8, scoring=scoring, legality=legality, leaf=leaf) + baseline = grow(b, fields, max_depth=4, max_leaves=8, legality=legality) + assert seen and len(leaf_calls) == len(tree.value) + assert set(tree.feature) <= {-1, 1} + np.testing.assert_allclose(tree.predict(x), 2 * baseline.predict(x)) + + +@pytest.mark.parametrize("grow", [best_first, symmetric]) +def test_recipe_substitution_and_persistence(grow, tmp_path): + from functools import partial + + from openboost import RunContext + from openboost.artifacts import Model + from openboost.recipes import squared + from tests.v1.reference.tree import boost_squared + + rng = np.random.default_rng(41) + x = NumericData(rng.integers(0, 5, (20, 3)), np.arange(20), ("a", "b", "c")) + p = Problem(x, rng.normal(size=(20, 1)), x.row_ids, weight=rng.uniform(0.5, 2, 20)) + result = squared( + p, + p, + context=RunContext(grow.__name__, 1), + rounds=3, + bins=5, + learner=partial(grow, max_depth=3, max_leaves=5), + ) + b = Binning.fit(x, bins=5).transform(x) + expected = boost_squared( + b.codes.T, + p.target[:, 0], + weight=p.weight, + rounds=3, + policy=grow.__name__, + max_depth=3, + max_leaves=5, + ) + np.testing.assert_allclose(result.state.train_raw[:, 0], expected.predict(b.codes.T)) + path = tmp_path / "ensemble.json" + result.state.model.save(path) + restored = Model.load(path) + unseen = NumericData( + [[-100, 1, 0], [100, 3, 5], [np.nan, np.nan, np.nan]], [1, 2, 3], ("a", "b", "c") + ) + np.testing.assert_array_equal(restored.predict(unseen), result.state.model.predict(unseen)) + assert restored.identity == result.state.model.identity + + +@pytest.mark.parametrize("grow", [best_first, symmetric]) +def test_invalid_callbacks_and_capacity_rejected(grow): + x = NumericData([[0], [1]], [1, 2], ("a",)) + p = Problem(x, [[0], [0]], x.row_ids) + b = Binning.fit(x, bins=2).transform(x) + fields = newton(p, [-1, 1], [1, 1]) + for kwargs in ( + {"max_depth": -1}, + {"max_leaves": 0}, + {"max_leaves": 2**31}, + {"scoring": lambda _: np.nan}, + {"leaf": lambda *_: np.inf}, + ): + with pytest.raises(ValueError): + grow(b, fields, **kwargs) diff --git a/tests/v1/test_public_multioutput.py b/tests/v1/test_public_multioutput.py new file mode 100644 index 0000000..4f6a221 --- /dev/null +++ b/tests/v1/test_public_multioutput.py @@ -0,0 +1,171 @@ +"""Multi-output regression policies, scaling and persisted original units.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.multioutput import MultiOutputModel, TargetScale +from openboost.objectives import MultiSquared +from openboost.recipes import multi_squared, squared +from openboost.tree import best_first, depthwise, symmetric +from tests.v1.reference.mixed import Transformer, grow + + +def fixture(): + x = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [0, 1, 2, 3, 4, 5], + ("x", "c"), + ("numeric", "categorical"), + ) + return Problem( + x, + [[-5, 3], [2, 8], [8, -4], [3, 2], [-2, 9], [15, -1]], + x.row_ids, + weight=[1, 3, 2, 0, 4, 2], + offset=np.arange(12.0).reshape(6, 2) / 10, + ) + + +@pytest.mark.parametrize("policy", [depthwise, best_first, symmetric]) +@pytest.mark.parametrize("mode", ["shared", "projected", "independent"]) +def test_three_rounds_match_independent_reference(policy, mode): + p = fixture() + projection = np.array([[1.0], [0.0]]) if mode == "projected" else None + actual = multi_squared( + p, + p, + context=RunContext("multi", 1), + rounds=3, + bins=4, + grower=policy, + mode="independent" if mode == "independent" else "shared", + projection=projection, + ) + raw = np.broadcast_to(np.average(p.target - p.offset, axis=0, weights=p.weight), (6, 2)).copy() + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + for step in actual.steps: + g = p.with_offset(raw) - p.target + np.testing.assert_allclose(step.gradient, g) + if mode == "independent": + delta = np.column_stack( + [ + grow( + p.data.values, + g[:, k : k + 1], + np.ones((6, 1)), + transform, + weight=p.weight, + policy=policy.__name__, + ).predict(p.data.values)[:, 0] + for k in range(2) + ] + ) + else: + delta = grow( + p.data.values, + g, + np.ones_like(g), + transform, + weight=p.weight, + policy=policy.__name__, + projection=projection, + ).predict(p.data.values) + np.testing.assert_allclose(step.raw_before, raw) + raw += 0.1 * delta + np.testing.assert_allclose(step.raw_after, raw) + expected = np.average((p.with_offset(raw) - p.target) ** 2, axis=0, weights=p.weight) + np.testing.assert_allclose(step.mse_after, expected) + assert actual.state.version == 3 + assert len(actual.state.model.terms) == (6 if mode == "independent" else 3) + + +def test_output_permutation_and_single_output_limit(): + p = fixture() + left = multi_squared(p, p, context=RunContext("left", 1), rounds=2) + reversed_p = replace(p, target=p.target[:, ::-1], offset=p.offset[:, ::-1]) + right = multi_squared(reversed_p, reversed_p, context=RunContext("right", 1), rounds=2) + np.testing.assert_allclose(left.state.train_raw[:, ::-1], right.state.train_raw) + single = replace(p, target=p.target[:, :1], offset=p.offset[:, :1], raw_width=1) + regular = squared(single, single, context=RunContext("scalar", 1)) + for mode in ("shared", "independent"): + result = multi_squared(single, single, context=RunContext(mode, 1), mode=mode) + np.testing.assert_allclose(result.state.train_raw, regular.state.train_raw) + + +def test_training_scaling_constant_and_original_units(): + p = fixture() + scale = TargetScale.fit(p) + np.testing.assert_allclose(scale.mean, np.average(p.target, weights=p.weight, axis=0)) + transformed = scale.transform(p) + np.testing.assert_allclose( + np.average(transformed.target, weights=p.weight, axis=0), 0, atol=1e-15 + ) + valid = replace(p, target=p.target + 100) + np.testing.assert_allclose( + scale.transform(valid).target, (valid.target - scale.mean) / scale.scale + ) + constant = replace(p, target=np.column_stack((p.target[:, 0], np.full(6, 7.0)))) + constant_scale = TargetScale.fit(constant) + assert constant_scale.constant == (False, True) and constant_scale.scale[1] == 1 + fit = multi_squared(transformed, transformed, context=RunContext("scaled", 1)) + model = MultiOutputModel(fit.state.model, scale) + np.testing.assert_allclose( + model.predict(p.data, offset=p.offset), + fit.state.model.predict(p.data) * scale.scale + scale.mean + p.offset, + ) + + +def test_wrong_target_kind_projection_and_atomic_rejection(): + p = fixture() + censored = replace(p, target=np.ones((6, 2)), target_kind="event_right") + with pytest.raises(ValueError): + MultiSquared.validate(censored) + for projection, mode in [ + (np.zeros((2, 1)), "shared"), + (np.ones((3, 1)), "shared"), + (np.ones((2, 1)), "independent"), + ]: + with pytest.raises(ValueError): + multi_squared(p, p, context=RunContext("bad", 1), projection=projection, mode=mode) + result = multi_squared( + p, p, context=RunContext("reject", 1), learning_rate=0, step="backtracking", rounds=2 + ) + assert result.state.version == 0 + assert all(not s.accepted and s.raw_before is s.raw_after for s in result.steps) + + +@pytest.mark.parametrize("mode", ["shared", "independent"]) +def test_fresh_process_scaled_roundtrip(tmp_path, mode): + import json + import subprocess + import sys + + p = fixture() + scale = TargetScale.fit(p) + q = scale.transform(p) + fit = multi_squared(q, q, context=RunContext("persist", 1), mode=mode) + model = MultiOutputModel(fit.state.model, scale) + path = tmp_path / "multi.json" + model.save(path) + assert MultiOutputModel.load(path).identity == model.identity + x = MixedData( + [[1, "unknown"], [None, None]], [10, 11], p.data.feature_names, p.data.feature_kinds + ) + code = """import json,sys +from openboost import MixedData +from openboost.multioutput import MultiOutputModel +x=MixedData([[1,'unknown'],[None,None]],[10,11],('x','c'),('numeric','categorical')) +print(json.dumps(MultiOutputModel.load(sys.argv[1]).predict(x,offset=[[.1,.2],[.3,.4]]).tolist())) +""" + output = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + np.testing.assert_array_equal(output, model.predict(x, offset=[[0.1, 0.2], [0.3, 0.4]])) + record = model.record() + record["scale"] = [0, 1] + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + MultiOutputModel.load(path) diff --git a/tests/v1/test_public_normal.py b/tests/v1/test_public_normal.py new file mode 100644 index 0000000..44f0342 --- /dev/null +++ b/tests/v1/test_public_normal.py @@ -0,0 +1,202 @@ +"""Normal distributional recipe against independent geometry and update oracles.""" + +import numpy as np + +from openboost import NumericData, Problem + + +def test_raw_width_is_independent_of_target_width(): + x = NumericData([[0], [1]], [1, 2], ("x",)) + p = Problem(x, [[2], [3]], x.row_ids, raw_width=2) + assert p.target.shape == (2, 1) and p.offset.shape == (2, 2) + np.testing.assert_array_equal(p.with_offset([[1, 2], [3, 4]]), [[1, 2], [3, 4]]) + + +import json +import subprocess +import sys + +import pytest + +from openboost import RunContext +from openboost.artifacts import Model +from openboost.binning import Binning +from openboost.objectives import Normal, Squared, diagonal_direction +from openboost.recipes import normal +from openboost.stats import least_squares +from openboost.tree import depthwise +from tests.v1.reference.coupled import directions, normal_base, normal_scores, step +from tests.v1.reference.coupled import normal as reference_normal + + +def fixture(): + x = NumericData([[0], [1], [2], [3], [4], [np.nan]], np.arange(6), ("x",)) + p = Problem( + x, [[-4], [-1], [0], [1], [3], [8]], x.row_ids, weight=[1, 0, 2, 1, 3, 2], raw_width=2 + ) + return p + + +@pytest.mark.parametrize("mode", ["ordinary", "natural"]) +def test_geometry_base_and_three_rounds_match_reference(mode): + p = fixture() + result = normal(p, p, context=RunContext(mode, 3), rounds=3, bins=6, mode=mode) + base = normal_base(p.target[:, 0], minimum_scale=1e-6, weight=p.weight) + np.testing.assert_allclose(result.state.model.base, base) + raw = np.broadcast_to(base, (6, 2)).copy() + b = Binning.fit(p.data, bins=6).transform(p.data) + bins = np.where(b.missing.T, np.nan, b.codes.T) + for actual in result.steps: + loss, gradient, metric = reference_normal(raw, p.target[:, 0], weight=p.weight) + expected_direction = directions( + gradient, metric, mode="full" if mode == "natural" else mode + ) + np.testing.assert_allclose(actual.gradient, gradient) + np.testing.assert_allclose(actual.fisher_diagonal, np.diagonal(metric, axis1=1, axis2=2)) + np.testing.assert_allclose(actual.direction, expected_direction) + ref = step( + bins, + raw, + p.target[:, 0], + reference_normal, + weight=p.weight, + mode="full" if mode == "natural" else mode, + rates=tuple(0.1 * 0.5**j for j in range(6)), + ) + assert actual.accepted == ref.accepted + assert actual.coefficients == tuple(t[0] for t in ref.trials) + np.testing.assert_allclose(actual.raw_before, raw) + np.testing.assert_allclose(actual.raw_after, ref.raw_after) + np.testing.assert_allclose([actual.loss_before, actual.loss_after], [loss, ref.loss_after]) + raw = np.asarray(ref.raw_after) + nll, crps = normal_scores(raw, p.target[:, 0], weight=p.weight) + assert Normal.loss(p, result.state.train_raw) == pytest.approx(nll) + assert np.isfinite(crps) + np.testing.assert_allclose(Normal.parameters(raw)[:, 1], np.exp(raw[:, 1])) + + +def test_offset_geometry_and_base_first_order_conditions(): + p = fixture() + offsets = np.column_stack((np.arange(6) / 2, np.arange(6) / 10)) + p = Problem(p.data, p.target, p.row_ids, weight=p.weight, offset=offsets, raw_width=2) + base = Normal.base(p) + raw = np.broadcast_to(base, (6, 2)) + loss, g, h = Normal.geometry(p, raw) + expected = reference_normal(raw + offsets, p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(g, expected[1]) + np.testing.assert_allclose(h, np.diagonal(expected[2], axis1=1, axis2=2)) + assert loss == pytest.approx(expected[0]) + np.testing.assert_allclose(np.sum(p.weight[:, None] * g, axis=0), [0, 0], atol=1e-12) + result = normal(p, p, context=RunContext("offsets", 1), rounds=2) + np.testing.assert_array_equal( + result.state.model.predict(p.data, offset=offsets), result.state.train_raw + offsets + ) + z = diagonal_direction(g, h) + fields = least_squares(p, z[:, 0]) + np.testing.assert_allclose(fields.values[:, 0], -p.weight * z[:, 0]) + np.testing.assert_allclose(fields.values[:, 1], p.weight) + + +def test_joint_full_rejection_and_nonfinite_backtracking(): + p = fixture() + calls = [] + + def zero(b, fields): + calls.append(1) + return depthwise(b, fields, max_depth=0, leaf=lambda *_: 0) + + result = normal(p, p, context=RunContext("reject", 1), learner=zero, rounds=2) + assert len(calls) == 4 and result.state.version == 0 + assert not result.state.model.terms and result.state.best_model is result.state.model + for item in result.steps: + assert not item.accepted and len(item.coefficients) == 6 + assert item.raw_after is item.raw_before + # Nonfinite distribution trials must not corrupt accepted state. + calls.clear() + result = normal(p, p, context=RunContext("overflow", 1), rounds=1, learning_rate=1e5) + assert any(result.steps[0].failures) + assert not result.steps[0].accepted and result.state.version == 0 + assert np.isfinite(result.state.train_raw).all() + + +def test_normal_model_roundtrip_without_training_data(tmp_path): + p = fixture() + result = normal(p, p, context=RunContext("persist", 1), rounds=2, step="fixed") + assert result.state.version == 2 and len(result.state.model.terms) == 4 + path = tmp_path / "normal.json" + result.state.model.save(path) + assert Model.load(path).identity == result.state.model.identity + code = """import json, sys +from openboost import NumericData +from openboost.artifacts import Model +from openboost.objectives import Normal +x = NumericData([[-100], [100], [float('nan')]], [1, 2, 3], ('x',)) +print(json.dumps(Normal.parameters(Model.load(sys.argv[1]).predict(x)).tolist())) +""" + output = subprocess.check_output([sys.executable, "-c", code, str(path)], text=True) + x = NumericData([[-100], [100], [np.nan]], [1, 2, 3], ("x",)) + np.testing.assert_allclose(json.loads(output), Normal.parameters(result.state.model.predict(x))) + + +@pytest.mark.parametrize( + "kwargs", + [ + {"raw_width": 0}, + {"raw_width": True}, + {"raw_width": 2, "offset": [[0], [0]]}, + {"raw_width": 1.5}, + ], +) +def test_invalid_raw_shapes_rejected(kwargs): + x = NumericData([[0], [1]], [1, 2], ("x",)) + with pytest.raises(ValueError): + Problem(x, [[1], [2]], x.row_ids, **kwargs) + + +def test_invalid_geometry_zero_rounds_and_scalar_recipe_boundary(): + p = fixture() + with pytest.raises(ValueError): + Squared.validate(p) + for kwargs in ( + {"mode": "full"}, + {"damping": -1}, + {"minimum_scale": 0}, + {"mode": "ordinary", "damping": 1}, + ): + with pytest.raises(ValueError): + normal(p, p, context=RunContext("invalid", 1), rounds=0, **kwargs) + with pytest.raises(ValueError): + Normal.parameters([[0, -1000]]) + ordinary = Problem(p.data, p.target, p.row_ids) + assert ordinary.identity != p.identity + + +def test_nonfinite_trials_continue_to_finite_trials_and_schema_errors_raise(): + p = fixture() + channels = [] + + def shrink_scale(b, fields): + value = 0 if not channels else -10 + channels.append(1) + return depthwise(b, fields, max_depth=0, leaf=lambda *_: value) + + result = normal( + p, + p, + context=RunContext("finite-retry", 1), + rounds=1, + learner=shrink_scale, + learning_rate=100, + ) + failures = result.steps[0].failures + assert len(channels) == 2 and failures[0] is not None and failures[-1] is None + assert not result.steps[0].accepted and result.state.version == 0 + + def wrong_schema(b, fields): + from dataclasses import replace + + tree = depthwise(b, fields) + return replace(tree, binning=Binning(("wrong",), tree.binning.cuts)) + + with pytest.raises(ValueError, match="schema"): + normal(p, p, context=RunContext("schema", 1), rounds=1, learner=wrong_schema) diff --git a/tests/v1/test_public_numeric_ops.py b/tests/v1/test_public_numeric_ops.py new file mode 100644 index 0000000..cf01628 --- /dev/null +++ b/tests/v1/test_public_numeric_ops.py @@ -0,0 +1,175 @@ +"""Compare public histogram operations with independent row-enumeration oracles.""" + +from functools import partial + +import numpy as np +import pytest + +from openboost import NumericData, Problem +from openboost.binning import Binning +from openboost.ops import candidates, choose, feasible, histogram, newton_leaf, partition, score +from openboost.stats import apply_weight, newton +from tests.v1.reference.data import NumericBinning as ReferenceBinning +from tests.v1.reference.scalar import newton_leaf as reference_leaf +from tests.v1.reference.tree import best_split, enumerate_splits + + +def setup(values, weight=None): + x = np.asarray(values, dtype=float) + data = NumericData(x, np.arange(len(x)) + 100, tuple(f"f{i}" for i in range(x.shape[1]))) + problem = Problem(data, np.zeros((len(x), 1)), data.row_ids, weight=weight) + binned = Binning.fit(data, bins=4).transform(data) + return problem, binned + + +@pytest.mark.parametrize( + "column,bins", + [([0, 0, 0, 1], 2), ([np.nan, np.nan], 4), ([3, 3, 3], 5), ([-4, -1, 2, 9], 4), ([1, 3], 1)], +) +def test_binning_matches_order_statistic_reference(column, bins): + data = NumericData(np.asarray(column)[:, None], np.arange(len(column)), ("x",)) + fitted = Binning.fit(data, bins=bins) + ref = ReferenceBinning.fit(column, bins=bins) + np.testing.assert_allclose(fitted.cuts[0], ref.cuts) + target = NumericData([[-100], [0], [100], [np.nan]], [1, 2, 3, 4], ("x",)) + got = fitted.transform(target) + codes, missing = ref.transform(target.values[:, 0]) + np.testing.assert_array_equal(got.codes[0], codes) + np.testing.assert_array_equal(got.missing[0], missing) + assert got.codes.dtype == np.int32 and got.codes.shape == (1, 4) + with pytest.raises(ValueError): + got.codes.flags.writeable = True + + +@pytest.mark.parametrize("rows", [None, [0, 2, 4, 5], []]) +def test_histogram_candidates_and_routing_match_exhaustive_oracle(rows): + p, b = setup( + [[0, 1], [0, np.nan], [1, 2], [2, 0], [np.nan, 1], [3, 3]], weight=[1, 0, 2, 1, 3, 1] + ) + g, h = np.array([-6, 1, 1, 1, 1, 2.0]), np.ones(6) + info = np.eye(2)[np.arange(6) % 2] + fields = newton(p, g, h).add_independent("a", info[:, 0]).add_independent("b", info[:, 1]) + hist = histogram(b, fields, rows) + selected = np.arange(6) if rows is None else np.asarray(rows, dtype=int) + np.testing.assert_allclose( + hist.total[:2], [np.dot(p.weight[selected], g[selected]), p.weight[selected].sum()] + ) + for counts, sums in zip(hist.counts, hist.sums, strict=True): + assert counts.sum() == len(selected) + np.testing.assert_allclose(sums.sum(axis=0), hist.total) + bins = np.where(b.missing.T, np.nan, b.codes.T) + ref = enumerate_splits( + bins, g, h, weight=p.weight, rows=selected, information=info, min_information=1 + ) + options = candidates(hist) + legal = partial(feasible, min_information={"a": 1, "b": 1}) + observed = {c.key: c for c in options if legal(c)} + assert set(observed) == { + (c.condition.feature, c.condition.threshold, c.condition.missing_left) for c in ref + } + for c in ref: + got = observed[(c.condition.feature, c.condition.threshold, c.condition.missing_left)] + assert score(got) == pytest.approx(c.gain, abs=1e-12) + left, right = partition(b, selected, got) + np.testing.assert_array_equal(left, c.left) + np.testing.assert_array_equal(right, c.right) + assert newton_leaf(got.left, got.names) == pytest.approx( + reference_leaf(got.left[0], got.left[1]) + ) + best, expected = choose(options, legality=legal), best_split(ref) + assert (best is None) == (expected is None) + if best: + assert best.key == ( + expected.condition.feature, + expected.condition.threshold, + expected.condition.missing_left, + ) + + +def test_weight_once_independent_mass_and_foreign_identity(): + p, b = setup([[0], [1], [2]], [0, 2, 3]) + fields = newton(p, [-1, 2, 3], [1, 1, 1]).add_independent("cohort", [1, 1, 1]) + np.testing.assert_array_equal(histogram(b, fields).total, [13, 5, 3]) + with pytest.raises(ValueError, match="already applied"): + apply_weight(fields, p) + other = NumericData(p.data.values, p.row_ids[::-1], p.data.feature_names) + with pytest.raises(ValueError, match="identity"): + histogram(b.binning.transform(other), fields) + + +def test_constraint_changes_winner_through_public_callback(): + p, b = setup(np.arange(6)[:, None]) + b = Binning.fit(p.data, bins=6).transform(p.data) + fields = newton(p, [-6, 1, 1, 1, 1, 2], np.ones(6)) + fields = fields.add_independent("a", [1, 0, 1, 0, 1, 0]).add_independent( + "b", [0, 1, 0, 1, 0, 1] + ) + options = candidates(histogram(b, fields)) + unconstrained = choose(options) + constrained = choose(options, legality=partial(feasible, min_information={"a": 1, "b": 1})) + assert unconstrained is not None and constrained is not None + assert unconstrained.key != constrained.key + left, right = partition(b, None, constrained) + for rows in (left, right): + assert fields.values[rows, 2:].sum(axis=0).min() >= 1 + with pytest.raises(ValueError, match="routed rows"): + partition(b, [0, 1], constrained) + with pytest.raises(ValueError, match="nonfinite"): + choose(options, scoring=lambda _: np.nan) + + +def test_two_level_operations_keep_original_row_positions(): + p, b = setup(np.arange(8)[:, None]) + fields = newton(p, [-4, -4, -1, -1, 1, 1, 4, 4], np.ones(8)) + root = choose(candidates(histogram(b, fields))) + children = partition(b, None, root) + leaves = [] + for rows in children: + child = choose(candidates(histogram(b, fields, rows))) + leaves.extend(partition(b, rows, child) if child else [rows]) + np.testing.assert_array_equal(np.sort(np.concatenate(leaves)), np.arange(8)) + assert len(leaves) == 4 + for rows in leaves: + total = histogram(b, fields, rows).total + expected = reference_leaf(fields.values[rows, 0].sum(), fields.values[rows, 1].sum()) + assert newton_leaf(total, fields.names) == expected + + +def test_missing_only_split_and_all_missing_feature(): + p, b = setup([[1, np.nan], [1, np.nan], [np.nan, np.nan], [np.nan, np.nan]]) + fields = newton(p, [-2, -2, 2, 2], np.ones(4)) + options = candidates(histogram(b, fields)) + assert all(c.feature == 0 for c in options) + chosen = choose(options) + left, right = partition(b, None, chosen) + np.testing.assert_array_equal(left, [0, 1]) + np.testing.assert_array_equal(right, [2, 3]) + assert chosen.key == (0, 0, False) + + +@pytest.mark.parametrize("rows", [[0, 0], [-1], [4], [1.5]]) +def test_invalid_routes_are_not_coerced(rows): + p, b = setup([[0], [1], [2]]) + with pytest.raises(ValueError, match="row positions"): + histogram(b, newton(p, [-1, 0, 1], np.ones(3)), rows) + + +def test_binning_identity_and_weight_role_cannot_be_reused_silently(): + p, b = setup([[0], [1], [2], [3]]) + fields = newton(p, [-2, -1, 1, 2], np.ones(4)) + options = candidates(histogram(b, fields)) + chosen = choose(options) + other = Binning.fit(p.data, bins=2).transform(p.data) + with pytest.raises(ValueError, match="different data"): + partition(other, None, chosen) + with pytest.raises(ValueError, match="independent"): + feasible(chosen, min_information={"curvature": 1}) + + +def test_overflowing_quantiles_cannot_silently_remove_cuts(): + data = NumericData([[-1e308], [1e308]], [1, 2], ("x",)) + with ( + np.errstate(over="ignore", invalid="ignore"), + pytest.raises(ValueError, match="interpolated cuts"), + ): + Binning.fit(data, bins=2) diff --git a/tests/v1/test_public_ordered.py b/tests/v1/test_public_ordered.py new file mode 100644 index 0000000..0c0004c --- /dev/null +++ b/tests/v1/test_public_ordered.py @@ -0,0 +1,195 @@ +"""Ordered development package against independent two-parameter references.""" + +from dataclasses import replace +from functools import partial + +import numpy as np +import pytest + +from openboost import NumericData, Problem, RunContext, recipes +from openboost.binning import Binning +from openboost.objectives import Normal, diagonal_direction +from openboost.runtime import initialize +from openboost.tree import depthwise +from tests.v1.reference.coupled import formula as reference_formula +from tests.v1.reference.coupled import normal as reference_normal +from tests.v1.reference.coupled import step +from tests.v1.test_public_extensions import ROOT, load + + +def extension(): + return load(ROOT / "ordered_updates/src/ob_ordered_updates/__init__.py", "ordered") + + +def problem(family): + x = NumericData([[0], [1], [2], [3], [4], [np.nan]], np.arange(6), ("feature",)) + return Problem( + x, + [[0.5], [1], [2], [3], [5], [8]], + x.row_ids, + weight=[1, 0, 2, 1, 3, 1], + raw_width=2, + offset=np.column_stack((np.arange(6) / 20, np.arange(6) / 50)), + structure={"x": np.linspace(0.2, 2, 6)[:, None]} if family == "formula" else {}, + ) + + +@pytest.mark.parametrize( + "family,mode", [("normal", "natural"), ("normal", "ordinary"), ("formula", "full")] +) +@pytest.mark.parametrize("order", [(0, 1), (1, 0)]) +def test_three_rounds_match_independent_reference(family, mode, order): + mod = extension() + p = problem(family) + kwargs = {"mode": mode} if family == "normal" else {} + fit = getattr(mod, family)( + p, p, context=RunContext("ordered", 7), rounds=3, bins=6, order=order, **kwargs + ) + binned = Binning.fit(p.data, bins=6).transform(p.data) + bins = np.where(binned.missing.T, np.nan, binned.codes.T) + raw = np.broadcast_to(fit.state.model.base, p.offset.shape).copy() + objective = ( + reference_normal + if family == "normal" + else partial(reference_formula, x=p.structure["x"][:, 0]) + ) + + def offset_objective(values, target, *, weight): + return objective(values + p.offset, target, weight=weight) + + accepted_count = 0 + for sweep in fit.steps: + assert tuple(s.channel for s in sweep) == order + for actual in sweep: + expected = step( + bins, + raw, + p.target[:, 0], + offset_objective, + weight=p.weight, + mode="ordinary" if mode == "ordinary" else "full", + damping=0.1 if family == "formula" else 0.0, + channels=(actual.channel,), + rates=tuple(0.1 * 0.5**j for j in range(6)), + ) + np.testing.assert_allclose(actual.before.train_raw, raw, atol=1e-10) + np.testing.assert_allclose(actual.after.train_raw, expected.raw_after, atol=1e-10) + assert actual.coefficients == tuple(t[0] for t in expected.trials) + assert (actual.after is not actual.before) == expected.accepted + accepted_count += expected.accepted + raw = np.array(expected.raw_after) + assert sweep[1].before is sweep[0].after + assert fit.state.version == accepted_count + assert fit.stop.completed_rounds == 3 + assert fit.state.best_score == pytest.approx( + min( + [fit.steps[0][0].before.best_score] + [s.after.best_score for r in fit.steps for s in r] + ) + ) + + +def test_reversed_order_is_a_different_algorithm(): + mod = extension() + for family in ("normal", "formula"): + p = problem(family) + forward = getattr(mod, family)(p, p, context=RunContext("order", 3), order=(0, 1)) + reverse = getattr(mod, family)(p, p, context=RunContext("order", 3), order=(1, 0)) + assert not np.allclose(forward.state.train_raw, reverse.state.train_raw) + joint = getattr(recipes, family)(p, p, context=RunContext("order", 3)) + assert not np.allclose(forward.state.train_raw, joint.state.train_raw) + + +@pytest.mark.parametrize("failure", ["reverse", "nan"]) +def test_rejected_candidates_preserve_state_best_and_rng(failure): + mod = extension() + p = problem("normal") + context = RunContext("reject", 19) + state = initialize(context, p, p, Normal.base(p), score=Normal.loss) + binned = Binning.fit(p.data, bins=6).transform(p.data) + seen = [] + + def geometry(problem, raw): + seen.append(raw) + return Normal.geometry(problem, raw) + + def loss(problem, raw): + if failure == "nan" and not np.array_equal(raw, state.train_raw): + return np.nan + return Normal.loss(problem, raw) + + def learner(data, fields): + tree = depthwise(data, fields, max_depth=1) + return replace(tree, value=-tree.value) if failure == "reverse" else tree + + final, steps = mod.sweep( + state, binned, geometry=geometry, direction=diagonal_direction, loss=loss, learner=learner + ) + assert final is state + assert all(s.before is state and s.after is state for s in steps) + assert all(len(s.coefficients) == 6 for s in steps) + assert all(raw is state.train_raw for raw in seen) + if failure == "nan": + assert all(s.failures == ("ValueError",) * 6 for s in steps) + np.testing.assert_array_equal( + context.rng(0, "leaf", "sample").random(5), final.context.rng(0, "leaf", "sample").random(5) + ) + + +def test_stopping_observes_outer_rounds_not_parameter_commits(): + mod = extension() + p = problem("normal") + fit = mod.normal(p, p, context=RunContext("stop", 1), rounds=10, patience=2, min_delta=100) + assert fit.stop.completed_rounds == 2 and fit.stop.reason == "patience" + assert len(fit.steps) == 2 and fit.state.version == 4 + assert fit.state.best_score < fit.stop.reference_score + + +def test_rejected_first_parameter_does_not_prevent_second_acceptance(): + mod = extension() + p = problem("normal") + state = initialize(RunContext("partial", 1), p, p, Normal.base(p), score=Normal.loss) + binned = Binning.fit(p.data, bins=6).transform(p.data) + calls = [] + + def learner(data, fields): + tree = depthwise(data, fields) + calls.append(fields) + return replace(tree, value=-tree.value) if len(calls) == 1 else tree + + final, steps = mod.sweep( + state, + binned, + geometry=Normal.geometry, + direction=diagonal_direction, + loss=Normal.loss, + learner=learner, + ) + assert steps[0].after is state and steps[1].before is state + assert final.version == 1 and steps[1].after is final + assert len(steps[0].coefficients) == 6 + + +def test_nonfinite_first_trial_can_recover_with_smaller_step(): + mod = extension() + p = problem("normal") + state = initialize(RunContext("recover", 2), p, p, Normal.base(p), score=Normal.loss) + binned = Binning.fit(p.data, bins=6).transform(p.data) + seen = [] + + def loss(problem, raw): + seen.append(raw) + return np.nan if len(seen) == 1 else Normal.loss(problem, raw) + + final, steps = mod.sweep( + state, binned, geometry=Normal.geometry, direction=diagonal_direction, loss=loss + ) + assert steps[0].coefficients == (0.1, 0.05) + assert steps[0].failures == ("ValueError", None) + assert final.version == 2 and steps[1].before is steps[0].after + + +@pytest.mark.parametrize("order", [(0,), (0, 0), (False, True), (0, 2)]) +def test_invalid_order_rejected_at_zero_rounds(order): + p = problem("normal") + with pytest.raises(ValueError, match="permutation"): + extension().normal(p, p, context=RunContext("bad", 1), rounds=0, order=order) diff --git a/tests/v1/test_public_poisson.py b/tests/v1/test_public_poisson.py new file mode 100644 index 0000000..5f87365 --- /dev/null +++ b/tests/v1/test_public_poisson.py @@ -0,0 +1,169 @@ +"""Poisson count/exposure mechanics compared with independent formulas and trees.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.objectives import Poisson +from openboost.outputs import poisson_mean +from openboost.recipes import poisson +from tests.v1.reference.mixed import Transformer, grow +from tests.v1.reference.positive import poisson as reference +from tests.v1.reference.positive import poisson_base + + +def fixture(): + x = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [0, 1, 2, 3, 4, 5], + ("x", "c"), + ("numeric", "categorical"), + ) + return Problem( + x, + [[0], [1], [4], [2], [0], [8]], + x.row_ids, + weight=[1, 2, 3, 0, 2, 1], + offset=np.arange(6.0)[:, None] / 10, + structure={"exposure": [[0.2], [0.5], [1], [2], [3], [0.8]]}, + ) + + +def test_geometry_initialization_and_finite_differences(): + p = fixture() + obj = Poisson() + e = p.structure["exposure"][:, 0] + base = obj.base(p)[0] + expected = poisson_base( + p.target[:, 0], e * np.exp(p.offset[:, 0]), weight=p.weight, minimum_rate=1e-6 + ) + np.testing.assert_allclose(base, expected) + raw = np.full((6, 1), base) + got = obj.geometry(p, raw) + ref = reference(p.with_offset(raw)[:, 0], p.target[:, 0], e, weight=p.weight) + for a, b in zip(got, ref, strict=True): + np.testing.assert_allclose(a, b) + np.testing.assert_allclose(np.dot(p.weight, got[1]), 0, atol=1e-12) + eps = 1e-4 + for i in [0, 1, 2]: + delta = np.zeros((6, 1)) + delta[i] = eps + plus, minus = obj.loss(p, raw + delta), obj.loss(p, raw - delta) + np.testing.assert_allclose( + (plus - minus) / (2 * eps), got[1][i] * p.weight[i] / p.weight.sum(), atol=1e-8 + ) + np.testing.assert_allclose( + (plus + minus - 2 * got[0]) / eps**2, + got[2][i] * p.weight[i] / p.weight.sum(), + atol=1e-6, + ) + + +def test_three_rounds_match_independent_tree_and_geometry(): + p = fixture() + fit = poisson(p, p, context=RunContext("poisson", 7), rounds=3, bins=4) + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + raw = np.full( + (6, 1), + poisson_base( + p.target[:, 0], + p.structure["exposure"][:, 0] * np.exp(p.offset[:, 0]), + weight=p.weight, + minimum_rate=1e-6, + ), + ) + for step in fit.steps: + loss, g, h = reference( + p.with_offset(raw)[:, 0], p.target[:, 0], p.structure["exposure"][:, 0], weight=p.weight + ) + np.testing.assert_allclose(step.gradient, g) + np.testing.assert_allclose(step.curvature, h) + np.testing.assert_allclose(step.loss_before, loss) + tree = grow(p.data.values, g[:, None], h[:, None], transform, weight=p.weight) + np.testing.assert_allclose(step.raw_before, raw) + raw += 0.1 * tree.predict(p.data.values) + np.testing.assert_allclose(step.raw_after, raw) + assert fit.state.version == 3 + + +def test_zero_counts_and_exposure_scaling(): + p = fixture() + zero = replace(p, target=np.zeros((6, 1))) + obj = Poisson(minimum_rate=1e-4) + assert obj.base(zero) == [np.log(1e-4)] + fit = poisson(zero, zero, context=RunContext("zero", 1), minimum_rate=1e-4) + assert np.isfinite(fit.state.train_raw).all() + raw = np.arange(6.0)[:, None] / 5 + e = p.structure["exposure"][:, 0] + one, two = poisson_mean(raw, e), poisson_mean(raw, 2 * e) + np.testing.assert_allclose(two["rate"], one["rate"]) + np.testing.assert_allclose(two["count_mean"], 2 * one["count_mean"]) + + +@pytest.mark.parametrize("kind", ["fraction", "negative", "zero_exposure", "missing", "extra"]) +def test_reject_invalid_support_and_roles(kind): + p = fixture() + if kind in ("fraction", "negative"): + p = replace(p, target=np.full((6, 1), 0.5 if kind == "fraction" else -1)) + else: + structure = dict(p.structure) + if kind == "zero_exposure": + structure["exposure"] = np.zeros((6, 1)) + elif kind == "missing": + structure = {} + else: + structure["ignored"] = np.ones((6, 1)) + p = replace(p, structure=structure) + with pytest.raises(ValueError): + poisson(p, p, context=RunContext("invalid", 1)) + + +def test_overflow_and_backtracking_rejection_are_explicit(): + p = fixture() + with pytest.raises((ValueError, FloatingPointError)): + Poisson().geometry(p, np.full((6, 1), 1000)) + with pytest.raises(ValueError): + poisson_mean(np.full((6, 1), -1000), np.ones(6)) + from openboost.tree import depthwise + + def zero(data, fields): + return depthwise(data, fields, max_depth=0, leaf=lambda *_: 0) + + fit = poisson( + p, p, context=RunContext("reject", 1), learner=zero, step="backtracking", rounds=2 + ) + assert fit.state.version == 0 + assert all(not s.accepted and s.raw_before is s.raw_after for s in fit.steps) + + +def test_fresh_process_count_rate_roundtrip(tmp_path): + import json + import subprocess + import sys + + from openboost.artifacts import Model + + p = fixture() + model = poisson(p, p, context=RunContext("persist", 1)).state.model + path = tmp_path / "model.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import MixedData +from openboost.artifacts import Model +from openboost.outputs import poisson_mean +x = MixedData([[1, 'unknown'], [None, None]], [10,11], ('x','c'), ('numeric','categorical')) +raw = Model.load(sys.argv[1]).predict(x, offset=[[0.1],[0.2]]) +print(json.dumps({k: v.tolist() for k,v in poisson_mean(raw, [0.5,2]).items()})) +""" + x = MixedData( + [[1, "unknown"], [None, None]], [10, 11], p.data.feature_names, p.data.feature_kinds + ) + expected = poisson_mean(model.predict(x, offset=[[0.1], [0.2]]), [0.5, 2]) + got = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + for key in expected: + np.testing.assert_array_equal(got[key], expected[key]) diff --git a/tests/v1/test_public_preparation.py b/tests/v1/test_public_preparation.py new file mode 100644 index 0000000..36ae3d6 --- /dev/null +++ b/tests/v1/test_public_preparation.py @@ -0,0 +1,105 @@ +"""Explicit training preparation reuse without sharing run state.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.binning import Binning, PreparedData, prepare_training +from openboost.recipes import multi_squared, normal, poisson, squared +from openboost.runs import RunSpec, run_many + + +def fixture(): + data = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [0, 1, 2, 3, 4, 5], + ("x", "c"), + ("numeric", "categorical"), + ) + y = np.array([1, 2, 4, 3, 8, 2])[:, None] + scalar = Problem(data, y, data.row_ids) + distribution = replace(scalar, raw_width=2, offset=np.zeros((6, 2))) + count = replace(scalar, structure={"exposure": np.ones((6, 1))}) + multi = replace( + scalar, target=np.column_stack((y, 2 * y)), raw_width=2, offset=np.zeros((6, 2)) + ) + return data, [ + (squared, scalar), + (normal, distribution), + (poisson, count), + (multi_squared, multi), + ] + + +@pytest.mark.parametrize("count", [1, 8, 32]) +def test_shared_independent_reordered_and_regrouped_runs(count, monkeypatch): + data, jobs = fixture() + prepared = PreparedData(data, bins=4) + specs = tuple( + RunSpec( + RunContext(f"job-{i}", i), + jobs[i % 4][1], + jobs[i % 4][1], + jobs[i % 4][0], + {"bins": 4, "rounds": 1 + i % 3}, + prepared=prepared, + ) + for i in range(count) + ) + independent = { + s.context.run_id: s.recipe(s.train, s.validation, context=s.context, **s.options) + for s in specs + } + + def forbidden(*args, **kwargs): + raise AssertionError("supplied preparation must not refit") + + monkeypatch.setattr(Binning, "fit", forbidden) + outcomes = run_many(specs) + reversed_results = {r.run_id: r for r in run_many(reversed(specs))} + regrouped = {r.run_id: r for group in (specs[::2], specs[1::2]) for r in run_many(group)} + for i, outcome in enumerate(outcomes): + assert outcome.error_type is None + state = outcome.result.state + assert state.identity == independent[outcome.run_id].state.identity + assert state.identity == reversed_results[outcome.run_id].result.state.identity + assert state.identity == regrouped[outcome.run_id].result.state.identity + for other in outcomes[:i]: + assert not np.shares_memory(state.train_raw, other.result.state.train_raw) + assert all(s.prepared is prepared for s in specs) + + +def test_preparation_identity_config_and_owned_codes(): + data, jobs = fixture() + prepared = PreparedData(data, 4) + assert prepare_training(data, bins=4, prepared=prepared) is prepared.binned + assert prepared.identity == PreparedData(data, 4).identity + assert prepared.identity != PreparedData(data, 3).identity + with pytest.raises(ValueError): + prepared.binned.codes.setflags(write=True) + changed = MixedData(data.values, data.row_ids + 10, data.feature_names, data.feature_kinds) + for d, bins in [(data, 3), (changed, 4), (data, True)]: + with pytest.raises(ValueError, match="identity"): + prepare_training(d, bins=bins, prepared=prepared) + # Target/weight changes may share exactly the same feature preparation. + p = replace(jobs[0][1], target=jobs[0][1].target + 3, weight=[1, 2, 3, 4, 5, 6]) + result = squared(p, p, context=RunContext("different-target", 1), bins=4, prepared=prepared) + expected = squared(p, p, context=RunContext("different-target", 1), bins=4) + assert result.state.identity == expected.state.identity + + +def test_failed_preparation_does_not_contaminate_other_run(): + data, jobs = fixture() + p = jobs[0][1] + prepared = PreparedData(data, 4) + specs = [ + RunSpec(RunContext("bad", 1), p, p, squared, {"bins": 3}, prepared), + RunSpec(RunContext("good", 2), p, p, squared, {"bins": 4}, prepared), + ] + results = run_many(specs) + assert results[0].error_type == "ValueError" and results[0].result is None + assert results[1].result.state.version == 2 + with pytest.raises(ValueError): + RunSpec(RunContext("ambiguous", 1), p, p, squared, {"prepared": None}, prepared) diff --git a/tests/v1/test_public_quantile.py b/tests/v1/test_public_quantile.py new file mode 100644 index 0000000..984aff6 --- /dev/null +++ b/tests/v1/test_public_quantile.py @@ -0,0 +1,162 @@ +"""Routed original-weight quantiles and D3 leaves against independent oracles.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.binning import Binning +from openboost.leaves import ResidualContext, quantile_leaf +from openboost.objectives import Quantile +from openboost.recipes import quantile +from openboost.tree import best_first, depthwise, symmetric +from tests.v1.reference.author import penalized_quantile +from tests.v1.reference.mixed import Transformer, grow +from tests.v1.reference.quantile import weighted_quantile + + +def fixture(): + x = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [10, 12, 14, 16, 18, 20], + ("x", "c"), + ("numeric", "categorical"), + ) + return Problem( + x, + [[-5], [2], [8], [3], [-2], [15]], + x.row_ids, + weight=[1, 3, 2, 0, 4, 2], + offset=np.arange(6.0)[:, None] / 4, + ) + + +@pytest.mark.parametrize("penalty", [0.0, 0.3, 5.0, 100.0]) +def test_leaf_optimum_matches_independent_enumeration(penalty): + p = fixture() + rng = np.random.default_rng(29) + for _ in range(20): + residual = rng.normal(size=6) + view = ResidualContext(p, residual).view(np.arange(6)) + anchor = -0.7 if penalty else 0 + got = quantile_leaf(view, q=0.7, penalty=penalty, anchor=anchor) + expected = ( + penalized_quantile(residual, 0.7, p.weight, penalty=penalty, anchor=anchor) + if penalty + else weighted_quantile(residual, 0.7, p.weight) + ) + np.testing.assert_allclose(got, expected, atol=1e-12) + + +@pytest.mark.parametrize("policy", [depthwise, best_first, symmetric]) +@pytest.mark.parametrize("penalty", [0.0, 2.0]) +def test_three_round_routed_leaves_match_independent_tree(policy, penalty): + p = fixture() + q, anchor = 0.7, (-1 if penalty else 0) + objective = Quantile(q) + fit = quantile( + p, + p, + context=RunContext("quantile", 1), + q=q, + penalty=penalty, + anchor=anchor, + rounds=3, + bins=4, + grower=policy, + ) + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + raw = np.full((6, 1), weighted_quantile((p.target - p.offset)[:, 0], q, p.weight)) + for step in fit.steps: + residual = (p.target - p.with_offset(raw))[:, 0] + tree = grow( + p.data.values, + ((residual < 0).astype(float) - q)[:, None], + np.ones((6, 1)), + transform, + weight=p.weight, + policy=policy.__name__, + ) + nodes = [] + for node in tree.nodes: + if node.condition is None: + rows = list(node.rows) + value = ( + penalized_quantile( + residual[rows], q, p.weight[rows], penalty=penalty, anchor=anchor + ) + if penalty + else weighted_quantile(residual[rows], q, p.weight[rows]) + ) + node = replace(node, value=np.array([value])) + nodes.append(node) + tree = replace(tree, nodes=tuple(nodes)) + np.testing.assert_allclose(step.raw_before, raw) + raw += 0.1 * tree.predict(p.data.values) + np.testing.assert_allclose(step.raw_after, raw) + np.testing.assert_allclose(step.loss_after, objective.loss(p, raw)) + assert fit.state.version == 3 + + +def test_routed_identity_original_weights_and_owned_rows(): + p = fixture() + objective = Quantile() + raw = np.zeros((6, 1)) + context = ResidualContext(p, objective.residuals(p, raw)) + seen = [] + + def solver(view, total, names): + seen.append(tuple(view.row_ids)) + positions = [list(p.row_ids).index(i) for i in view.row_ids] + np.testing.assert_array_equal(view.weight, p.weight[positions]) + np.testing.assert_array_equal(view.residual, context.residual[positions]) + with pytest.raises(ValueError): + view.weight.setflags(write=True) + return quantile_leaf(view) + + b = Binning.fit(p.data, bins=4).transform(p.data) + depthwise(b, objective.fields(p, raw), row_leaf=solver, leaf_context=context) + assert len(seen) > 1 and any(len(rows) < 6 for rows in seen) + foreign = ResidualContext(replace(p, offset=np.ones((6, 1))), context.residual) + with pytest.raises(ValueError, match="match problem"): + depthwise(b, objective.fields(p, raw), row_leaf=solver, leaf_context=foreign) + with pytest.raises(ValueError): + context.view([0, 0]) + with pytest.raises(ValueError): + depthwise(b, objective.fields(p, raw), row_leaf=solver) + + +def test_penalty_uses_original_mass_and_backtracking_can_reject(): + p = fixture() + context = ResidualContext(p, np.arange(6.0) + 1) + view = context.view(np.arange(6)) + small = quantile_leaf(view, penalty=1, anchor=-10) + large = quantile_leaf(view, penalty=100, anchor=-10) + assert abs(large + 10) < abs(small + 10) + scaled = replace(view, weight=view.weight * 10) + assert quantile_leaf(scaled, penalty=1, anchor=-10) != small + fit = quantile( + p, p, context=RunContext("reject", 1), max_depth=0, rounds=2, step="backtracking" + ) + assert fit.state.version == 0 + assert all(not s.accepted and s.raw_before is s.raw_after for s in fit.steps) + + +def test_persist_penalized_mixed_model(tmp_path): + from openboost.artifacts import Model + + p = fixture() + fit = quantile(p, p, context=RunContext("persist", 1), penalty=2, anchor=-1) + path = tmp_path / "model.json" + fit.state.model.save(path) + loaded = Model.load(path) + x = MixedData( + [[0, "unknown"], [None, None], [10, "a"]], + [30, 31, 32], + p.data.feature_names, + p.data.feature_kinds, + ) + np.testing.assert_array_equal(loaded.predict(x), fit.state.model.predict(x)) diff --git a/tests/v1/test_public_ranking.py b/tests/v1/test_public_ranking.py new file mode 100644 index 0000000..bfda317 --- /dev/null +++ b/tests/v1/test_public_ranking.py @@ -0,0 +1,176 @@ +"""Ranking geometry and round composition against independent query oracles.""" + +import numpy as np +import pytest + +from openboost import NumericData, Problem, RunContext +from openboost.ranking import Ranking +from openboost.recipes import ranking +from tests.v1.reference.ranking import pairwise, query_ndcg + + +def problem(): + x = NumericData([[0], [2], [1], [3], [4], [5]], [6, 2, 3, 4, 5, 1], ("x",)) + return Problem( + x, + [[2], [0], [1], [0], [2], [1]], + x.row_ids, + structure={ + "query": [[0], [0], [0], [1], [1], [1]], + "query_weight": [[2], [2], [2], [0.5], [0.5], [0.5]], + }, + ) + + +@pytest.mark.parametrize("lambdas", [False, True]) +def test_geometry_matches_query_oracle(lambdas): + p = problem() + raw = np.array([[0], [1], [1], [-1], [2], [0.0]]) + objective = Ranking(lambdas=lambdas, k=2) + result = objective.geometry(p, raw) + ref = pairwise( + raw[:, 0], + p.target[:, 0], + p.structure["query"][:, 0].astype(int), + row_ids=p.row_ids, + query_weight={0: 2, 1: 0.5}, + lambdas=lambdas, + k=2, + ) + np.testing.assert_allclose(result.gradient, ref.gradient) + np.testing.assert_allclose(result.curvature, ref.curvature) + np.testing.assert_allclose(result.loss, ref.loss) + + +@pytest.mark.parametrize("lambdas", [False, True]) +def test_three_rounds_recompute_geometry_and_match_independent_tree(lambdas): + from tests.v1.reference.mixed import Transformer, grow + + p = problem() + fit = ranking( + p, p, context=RunContext("rank", 7), lambdas=lambdas, rounds=3, bins=4, k=2, learning_rate=2 + ) + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + raw = np.zeros((6, 1)) + for step in fit.steps: + expected = pairwise( + raw[:, 0], + p.target[:, 0], + [0, 0, 0, 1, 1, 1], + row_ids=p.row_ids, + query_weight={0: 2, 1: 0.5}, + lambdas=lambdas, + k=2, + ) + np.testing.assert_allclose(step.gradient, expected.gradient) + np.testing.assert_allclose(step.curvature, expected.curvature) + tree = grow( + p.data.values, expected.gradient[:, None], expected.curvature[:, None], transform + ) + np.testing.assert_allclose(step.raw_before, raw) + raw += 2 * tree.predict(p.data.values) + np.testing.assert_allclose(step.raw_after, raw) + assert fit.state.version == 3 + assert not np.allclose(fit.steps[0].gradient, fit.steps[1].gradient) + objective = Ranking(lambdas=lambdas, k=2) + expected_ndcg = ( + sum( + w * query_ndcg(raw[rows, 0], p.target[rows, 0], row_ids=p.row_ids[rows], k=2) + for rows, w in [(slice(0, 3), 2), (slice(3, 6), 0.5)] + ) + / 2.5 + ) + np.testing.assert_allclose(objective.score(p, raw), 1 - expected_ndcg) + + +def test_query_isolation_offsets_and_tie_permutation(): + from dataclasses import replace + + p = problem() + raw = np.zeros((6, 1)) + objective = Ranking(lambdas=True, k=2) + base = objective.geometry(p, raw) + changed = raw.copy() + changed[3:] = [[5], [-3], [1]] + np.testing.assert_array_equal(objective.geometry(p, changed).gradient[:3], base.gradient[:3]) + shifted = replace(p, offset=changed) + np.testing.assert_array_equal( + objective.geometry(shifted, raw).gradient, objective.geometry(p, changed).gradient + ) + order = np.array([5, 2, 0, 4, 1, 3]) + x = NumericData(p.data.values[order], p.row_ids[order], p.data.feature_names) + shuffled = Problem( + x, + p.target[order], + x.row_ids, + structure={key: value[order] for key, value in p.structure.items()}, + ) + np.testing.assert_allclose(objective.geometry(shuffled, raw).gradient, base.gradient[order]) + np.testing.assert_allclose(objective.score(shuffled, raw), objective.score(p, raw)) + + +def test_logistic_derivatives_and_degenerate_queries(): + from dataclasses import replace + + p = problem() + objective = Ranking() + raw = np.arange(6.0)[:, None] / 3 + result = objective.geometry(p, raw) + epsilon = 1e-4 + for i in range(6): + delta = np.zeros_like(raw) + delta[i] = epsilon + plus = objective.geometry(p, raw + delta).loss + minus = objective.geometry(p, raw - delta).loss + np.testing.assert_allclose((plus - minus) / (2 * epsilon), result.gradient[i], atol=1e-8) + np.testing.assert_allclose( + (plus + minus - 2 * result.loss) / epsilon**2, result.curvature[i], atol=1e-7 + ) + flat = replace(p, target=np.zeros((6, 1))) + assert objective.geometry(flat, raw).loss == 0 + assert objective.score(flat, raw) == 0 + + +@pytest.mark.parametrize("change", ["weight", "query", "query_weight", "structure", "target"]) +def test_invalid_roles_rejected(change): + from dataclasses import replace + + p = problem() + if change == "weight": + p = replace(p, weight=np.full(6, 2)) + elif change == "target": + p = replace(p, target=np.full((6, 1), 0.5)) + else: + roles = dict(p.structure) + if change == "structure": + roles["ignored"] = np.ones((6, 1)) + else: + roles[change] = np.arange(6.0)[:, None] / 3 + p = replace(p, structure=roles) + with pytest.raises(ValueError): + ranking(p, p, context=RunContext("invalid", 1)) + + +def test_score_model_roundtrip_needs_no_query_roles(tmp_path): + import json + import subprocess + import sys + + from openboost.artifacts import Model + + p = problem() + model = ranking(p, p, context=RunContext("persist-ranking", 1), rounds=2).state.model + path = tmp_path / "ranking.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import NumericData +from openboost.artifacts import Model +x = NumericData([[0], [float('nan')], [5]], [10, 11, 12], ('x',)) +print(json.dumps(Model.load(sys.argv[1]).predict(x).tolist())) +""" + x = NumericData([[0], [np.nan], [5]], [10, 11, 12], ("x",)) + output = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + np.testing.assert_array_equal(output, model.predict(x)) diff --git a/tests/v1/test_public_results.py b/tests/v1/test_public_results.py new file mode 100644 index 0000000..83543f0 --- /dev/null +++ b/tests/v1/test_public_results.py @@ -0,0 +1,109 @@ +"""External result interoperability and fail-closed result validation.""" + +from dataclasses import replace +from types import SimpleNamespace + +import numpy as np +import pytest + +from openboost import NumericData, Problem, RunContext +from openboost.binning import Binning, PreparedData +from openboost.recipes import squared +from openboost.runs import RunSpec, run_many +from openboost.stopping import StopState +from tests.v1.test_public_ordered import extension + + +def problems(): + x = NumericData(np.arange(6)[:, None], np.arange(6), ("x",)) + p = Problem(x, [[0.5], [1], [2], [3], [5], [8]], x.row_ids) + return p, replace(p, raw_width=2, offset=np.zeros((6, 2))) + + +def test_external_ordered_result_is_preserved(): + _, p = problems() + recipe = extension().normal + context = RunContext("external", 7) + expected = recipe(p, p, context=context, rounds=2) + outcome = run_many([RunSpec(context, p, p, lambda *args, **kwargs: expected)])[0] + assert outcome.error_type is None + assert outcome.result is expected + assert outcome.result.state.version == 4 + assert outcome.result.stop.completed_rounds == len(outcome.result.steps) == 2 + + +@pytest.mark.parametrize( + "corruption", ["missing", "state", "stop", "unfinished", "trace", "list", "foreign"] +) +def test_invalid_result_fails_only_its_run(corruption): + p, _ = problems() + context = RunContext("bad", 7) + fit = squared(p, p, context=context, rounds=1) + parts = dict(state=fit.state, steps=fit.steps, stop=fit.stop) + if corruption == "missing": + del parts["stop"] + elif corruption == "state": + parts["state"] = object() + elif corruption == "stop": + parts["stop"] = object() + elif corruption == "unfinished": + parts["stop"] = StopState.start(1, rounds=2).observe(0.5) + elif corruption == "trace": + parts["steps"] = () + elif corruption == "list": + parts["steps"] = list(fit.steps) + else: + parts["state"] = replace(fit.state, context=RunContext("foreign", 7)) + bad = SimpleNamespace(**parts) + outcomes = run_many( + [ + RunSpec(context, p, p, lambda *a, **kw: bad), + RunSpec(RunContext("good", 7), p, p, squared, {"rounds": 1}), + ] + ) + assert outcomes[0].error_type == "ValueError" and outcomes[0].result is None + assert outcomes[1].error_type is None and outcomes[1].result.stop.reason == "budget" + + +@pytest.mark.parametrize("count", [1, 8, 32]) +def test_external_and_builtin_shared_runs(count, monkeypatch): + p, normal = problems() + prepared = PreparedData(p.data, bins=6) + external = extension().normal + specs = [] + expected = {} + for i in range(count): + train, recipe = (p, squared) if i % 2 else (normal, external) + valid = replace(train, target=train.target[::-1]) + context = RunContext(f"mixed-{i}", 7) + options = {"rounds": 6, "bins": 6, "patience": 1 + i % 3, "min_delta": 100.0} + specs.append(RunSpec(context, train, valid, recipe, options, prepared)) + expected[context.run_id] = recipe(train, valid, context=context, **options) + if count > 1: + assert len({r.stop.completed_rounds for r in expected.values()}) > 1 + + def forbidden(*a, **kw): + raise AssertionError("shared preparation must not refit") + + monkeypatch.setattr(Binning, "fit", forbidden) + failed = replace(specs[0], context=RunContext("failure", 7), recipe=lambda *a, **kw: object()) + executions = [ + run_many([failed, *specs]), + run_many(reversed(specs)), + tuple(r for group in (specs[::2], specs[1::2]) for r in run_many(group)), + run_many(specs), + ] + assert executions[0][0].error_type == "ValueError" + for outcomes in executions: + for out in outcomes: + if out.run_id == "failure": + continue + assert out.error_type is None + ref = expected[out.run_id] + assert out.result.state.identity == ref.state.identity + assert out.result.stop == ref.stop + np.testing.assert_array_equal(out.result.state.validation_raw, ref.state.validation_raw) + np.testing.assert_array_equal( + out.result.state.context.rng(1, "leaf", "sample").random(5), + ref.state.context.rng(1, "leaf", "sample").random(5), + ) diff --git a/tests/v1/test_public_squared.py b/tests/v1/test_public_squared.py new file mode 100644 index 0000000..c9b51c6 --- /dev/null +++ b/tests/v1/test_public_squared.py @@ -0,0 +1,200 @@ +"""Complete public squared recipe compared with independent reference traces.""" + +import numpy as np + +from openboost import NumericData, Problem, RunContext +from openboost.binning import Binning +from openboost.recipes import squared +from tests.v1.reference.tree import boost_squared + + +def test_two_round_trace_matches_reference(): + data = NumericData([[0], [1], [2], [3], [4], [np.nan]], np.arange(6), ("x",)) + p = Problem(data, [[-4], [-2], [0], [1], [3], [5]], data.row_ids, weight=[1, 0, 2, 1, 3, 1]) + result = squared(p, p, context=RunContext("squared", 3), rounds=3, bins=6) + b = Binning.fit(data, bins=6).transform(data) + expected = boost_squared( + np.where(b.missing.T, np.nan, b.codes.T), p.target[:, 0], weight=p.weight, rounds=3 + ) + assert result.state.version == 3 + assert result.state.model.base[0] == expected.base + for actual, ref in zip(result.steps, expected.steps, strict=True): + np.testing.assert_allclose(actual.gradient, ref.gradient) + np.testing.assert_allclose(actual.raw_before[:, 0], ref.raw_before) + np.testing.assert_allclose(actual.raw_after[:, 0], ref.raw_after) + np.testing.assert_allclose( + [actual.loss_before, actual.loss_after], [ref.loss_before, ref.loss_after] + ) + np.testing.assert_allclose( + result.state.train_raw[:, 0], expected.predict(np.where(b.missing.T, np.nan, b.codes.T)) + ) + + +def test_offsets_weights_and_validation_best_are_separate(): + from openboost.objectives import Squared + + x = NumericData([[0], [1], [2], [3]], [1, 2, 3, 4], ("x",)) + offset = np.array([[10], [20], [30], [40]]) + p = Problem(x, [[6], [18], [32], [44]], x.row_ids, weight=[1, 2, 3, 4], offset=offset) + shifted = Problem(x, p.target - offset, x.row_ids, weight=p.weight) + valid_x = NumericData([[0], [3]], [91, 92], ("x",)) + valid = Problem(valid_x, [[100], [-100]], valid_x.row_ids) + a = squared(p, valid, context=RunContext("offset", 1), rounds=3, learning_rate=1) + b = squared(shifted, valid, context=RunContext("shifted", 1), rounds=3, learning_rate=1) + np.testing.assert_allclose(a.state.train_raw, b.state.train_raw) + np.testing.assert_allclose(a.state.model.predict(x, offset=offset), b.state.train_raw + offset) + assert a.state.best_model is not a.state.model + candidates = [s.loss_after for s in a.steps] + assert candidates[-1] < candidates[0] + assert a.state.best_score <= Squared.loss(valid, a.state.validation_raw) + assert a.state.train_raw.shape == (4, 1) and a.state.validation_raw.shape == (2, 1) + + +def test_backtracking_reuses_learner_and_full_rejection_is_atomic(): + from openboost.tree import depthwise + + x = NumericData([[0], [1], [2], [3]], [1, 2, 3, 4], ("x",)) + p = Problem(x, [[-3], [-3], [3], [3]], x.row_ids) + calls = [] + + def fit(binned, fields): + calls.append(1) + return depthwise(binned, fields) + + result = squared( + p, + p, + context=RunContext("line-search", 1), + rounds=1, + learning_rate=16, + step="backtracking", + learner=fit, + ) + assert len(calls) == 1 and result.steps[0].coefficients == (16, 8, 4, 2) + assert result.steps[0].accepted and result.state.version == 1 + assert result.state.model.terms[0].coefficient == 2 + rejected = squared( + p, + p, + context=RunContext("rejected", 1), + rounds=2, + learning_rate=1024, + step="backtracking", + max_trials=6, + learner=fit, + ) + assert len(calls) == 3 + assert not rejected.state.model.terms and rejected.state.version == 0 + assert rejected.state.best_model is rejected.state.model + for step in rejected.steps: + assert not step.accepted and len(step.coefficients) == 6 + assert step.raw_before is step.raw_after + assert step.loss_before == step.loss_after + + +def test_mapped_tree_terms_atomic_and_fresh_process_persistence(tmp_path): + import subprocess + import sys + + from openboost.artifacts import ConstantTerm, Model, TreeTerm + from openboost.runtime import initialize, propose_terms, resolve + from openboost.stats import newton + from openboost.tree import depthwise + + x = NumericData([[0], [1], [np.nan]], [1, 2, 3], ("x",)) + scalar = Problem(x, [[0], [0], [0]], x.row_ids) + b = Binning.fit(x, bins=2).transform(x) + tree = depthwise(b, newton(scalar, [-2, 0, 2], [1, 1, 1])) + p = Problem(x, [[1, 2], [3, 4], [5, 6]], x.row_ids) + + def metric(p, raw): + return float(np.sum((p.with_offset(raw) - p.target) ** 2)) + + initial = initialize(RunContext("mapped", 2), p, p, [1, 2], score=metric) + mapping = np.array([[1.0, -2.0]]) + term = TreeTerm(tree, mapping, 0.5) + mapping[:] = 99 + proposal = propose_terms(initial, (ConstantTerm([2, 3]), term)) + assert resolve(initial, proposal, accept=False, score=metric) is initial + final = resolve(initial, proposal, accept=True, score=metric) + assert final.version == 1 and len(final.model.terms) == 2 + expected = np.array([3, 5]) + 0.5 * tree.predict(x) @ np.array([[1, -2]]) + np.testing.assert_allclose(final.train_raw, expected) + path = tmp_path / "ensemble.json" + final.model.save(path) + restored = Model.load(path) + assert restored.identity == final.model.identity + np.testing.assert_allclose(restored.predict(x), expected) + code = """import json, sys +from openboost import NumericData +from openboost.artifacts import Model +x = NumericData([[-100], [100], [float('nan')]], [11, 12, 13], ('x',)) +print(json.dumps(Model.load(sys.argv[1]).predict(x, offset=[[1, 2], [3, 4], [5, 6]]).tolist())) +""" + import json + + output = subprocess.check_output([sys.executable, "-c", code, str(path)], text=True) + unseen = NumericData([[-100], [100], [np.nan]], [11, 12, 13], ("x",)) + np.testing.assert_allclose( + json.loads(output), final.model.predict(unseen, offset=[[1, 2], [3, 4], [5, 6]]) + ) + + +def test_nested_tree_artifact_corruption(tmp_path): + import json + + import pytest + + from openboost.artifacts import Model + + x = NumericData([[0], [1]], [1, 2], ("x",)) + p = Problem(x, [[-2], [2]], x.row_ids) + model = squared(p, p, context=RunContext("persist", 1), rounds=1).state.model + for field, bad in (("mapping", [[1, 2]]), ("coefficient", float("nan")), ("kind", "unknown")): + record = model.record() + record["terms"][0][field] = bad + path = tmp_path / "bad.json" + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + Model.load(path) + record = model.record() + record["terms"][0]["learner"]["left"][0] = 0 + path.write_text(json.dumps(record)) + with pytest.raises(ValueError, match="cycle"): + Model.load(path) + + +def test_zero_rounds_still_validate_options_and_target(): + import pytest + + x = NumericData([[0], [1]], [1, 2], ("x",)) + p = Problem(x, [[-2], [2]], x.row_ids) + for kwargs in ( + {"rounds": -1}, + {"max_depth": -1}, + {"bins": 0}, + {"reg_lambda": -1}, + {"step": "unknown"}, + {"max_trials": 7}, + {"learning_rate": np.nan}, + {"learner": lambda *_: None, "max_depth": 3}, + ): + with pytest.raises(ValueError): + squared(p, p, context=RunContext("invalid", 1), **({"rounds": 0} | kwargs)) + wide = Problem(x, [[1, 2], [3, 4]], x.row_ids) + with pytest.raises(ValueError, match="scalar"): + squared(wide, wide, context=RunContext("wide", 1), rounds=0) + + +def test_direct_proposal_owns_term_sequence(): + import pytest + + from openboost.artifacts import ConstantTerm + from openboost.runtime import Proposal + + source = [ConstantTerm([1])] + proposal = Proposal("parent", source) + source.clear() + assert len(proposal.terms) == 1 + with pytest.raises(ValueError): + Proposal("parent", []) diff --git a/tests/v1/test_public_stopping.py b/tests/v1/test_public_stopping.py new file mode 100644 index 0000000..a429a22 --- /dev/null +++ b/tests/v1/test_public_stopping.py @@ -0,0 +1,262 @@ +"""Independent stopping oracles and public recipe integration.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import ClassSchema, NumericData, Problem, RunContext, recipes +from openboost.binning import Binning, PreparedData +from openboost.objectives import Normal, Squared +from openboost.runs import RunSpec, run_many +from openboost.stopping import StopState + + +def test_threshold_reference_ties_and_budget(): + state = StopState.start(10.0, rounds=8, patience=2, min_delta=1.0) + # Neither strict improvement nor the cumulative threshold is confused with + # best-model selection: 9 ties the threshold, 8.5 crosses it, 8 does not. + expected = [(9.0, 10.0, 1), (8.5, 8.5, 0), (8.0, 8.5, 1), (8.0, 8.5, 2)] + for i, (score, reference, stale) in enumerate(expected, 1): + previous = state + state = state.observe(score) + assert state.completed_rounds == i + assert state.reference_score == reference + assert state.stale_rounds == stale + assert previous.completed_rounds == i - 1 + assert state.reason == "patience" + with pytest.raises(ValueError, match="finished"): + state.observe(7.0) + assert StopState.start(1, rounds=0).reason == "budget" + assert StopState.start(1, rounds=1, patience=1).observe(1).reason == "patience" + assert StopState.start(1, rounds=1).observe(2).reason == "budget" + + +@pytest.mark.parametrize( + "options", + [ + {"rounds": -1}, + {"rounds": True}, + {"rounds": 1, "patience": 0}, + {"rounds": 1, "patience": True}, + {"rounds": 1, "patience": 1, "min_delta": -1}, + {"rounds": 1, "patience": 1, "min_delta": float("nan")}, + {"rounds": 1, "min_delta": 1}, + ], +) +def test_invalid_policy(options): + with pytest.raises(ValueError): + StopState.start(1, **options) + + +@pytest.mark.parametrize("score", [float("nan"), float("inf"), -float("inf"), True]) +def test_invalid_observations(score): + with pytest.raises(ValueError): + StopState.start(score, rounds=2) + initial = StopState.start(1, rounds=2) + with pytest.raises(ValueError): + initial.observe(score) + assert initial.completed_rounds == 0 + + +def problem(): + x = NumericData([[0], [1], [2], [3], [4], [5]], np.arange(6), ("x",)) + return Problem(x, [[1], [2], [3], [5], [7], [9]], x.row_ids) + + +@pytest.mark.parametrize( + "name", + [ + "squared", + "normal", + "formula", + "binary", + "multiclass", + "ranking", + "quantile", + "poisson", + "gamma", + "tweedie", + "aft", + "multi_squared", + ], +) +def test_every_recipe_observes_rejected_or_accepted_rounds_and_zero_budget(name): + p = problem() + if name in ("normal", "formula"): + p = replace( + p, + raw_width=2, + offset=np.zeros((6, 2)), + structure={"x": np.arange(1, 7)[:, None]} if name == "formula" else {}, + ) + elif name in ("binary", "multiclass"): + k = 2 if name == "binary" else 3 + p = replace( + p, + target=(np.arange(6) % k)[:, None], + classes=ClassSchema(tuple(range(k))), + raw_width=1 if k == 2 else k, + offset=np.zeros((6, 1 if k == 2 else k)), + ) + elif name == "ranking": + p = replace( + p, + target=(np.arange(6) % 3)[:, None], + structure={"query": [[0], [0], [0], [1], [1], [1]]}, + ) + elif name == "poisson": + p = replace(p, structure={"exposure": np.ones((6, 1))}) + elif name == "aft": + p = replace(p, target=np.column_stack((p.target, p.target)), target_kind="event_right") + elif name == "multi_squared": + p = replace( + p, + target=np.column_stack((p.target, p.target * 2)), + raw_width=2, + offset=np.zeros((6, 2)), + ) + recipe = getattr(recipes, name) + context = RunContext(name, 3) + initial = recipe(p, p, context=context, rounds=0, patience=2) + assert initial.stop.reason == "budget" and initial.steps == () + fit = recipe(p, p, context=context, rounds=7, patience=2, learning_rate=0) + assert fit.stop.reason == "patience" and fit.stop.completed_rounds == 2 + assert len(fit.steps) == 2 + assert fit.stop.last_score == initial.state.best_score + assert fit.state.best_model.identity == initial.state.best_model.identity + np.testing.assert_array_equal(fit.state.train_raw, initial.state.train_raw) + with pytest.raises(ValueError, match="patience"): + recipe(p, p, context=context, rounds=0, patience=0) + + +def test_validation_not_training_controls_stopping_and_strict_best_ignores_delta(): + p = problem() + v = replace(p, target=p.target[::-1]) + context = RunContext("validation", 7) + fit = recipes.squared(p, v, context=context, rounds=9, patience=2) + assert fit.stop.reason == "patience" and fit.stop.completed_rounds == 2 + assert fit.state.version == 2 + assert fit.steps[-1].loss_after < fit.steps[0].loss_before + assert fit.state.best_model.terms == () + assert Squared.loss(v, fit.state.validation_raw) > fit.state.best_score + improving = recipes.squared(p, p, context=context, rounds=9, patience=2, min_delta=100) + assert improving.stop.completed_rounds == 2 + assert improving.state.best_model.identity == improving.state.model.identity + assert improving.state.best_score < improving.stop.reference_score + + +def test_backtracking_trials_do_not_consume_patience_or_mutate_accepted_state(): + p = problem() + context = RunContext("reject", 12) + initial = recipes.squared(p, p, context=context, rounds=0) + fit = recipes.squared( + p, + p, + context=context, + rounds=8, + patience=2, + learning_rate=0, + step="backtracking", + max_trials=6, + ) + assert fit.stop.completed_rounds == 2 and fit.state.version == 0 + assert [len(s.coefficients) for s in fit.steps] == [6, 6] + assert not any(s.accepted for s in fit.steps) + assert fit.state.identity == initial.state.identity + np.testing.assert_array_equal( + context.rng(2, "leaf", "draw").random(4), fit.state.context.rng(2, "leaf", "draw").random(4) + ) + + +@pytest.mark.parametrize("count", [1, 8, 32]) +def test_shared_runs_different_validation_stops_reorder_retry_and_failures(count, monkeypatch): + p = problem() + prepared = PreparedData(p.data, 4) + specs = [] + expected = {} + expected_rounds = {} + for i in range(count): + train = p if i % 2 == 0 else replace(p, raw_width=2, offset=np.zeros((6, 2))) + valid = replace(train, target=train.target[::-1]) + recipe, loss = ( + (recipes.squared, Squared.loss) if i % 2 == 0 else (recipes.normal, Normal.loss) + ) + context = RunContext(f"job-{i}", 42) + patience = 1 + i % 3 + baseline = recipe(train, valid, context=context, rounds=0, bins=4).state.best_score + # Independent scan of a fixed-budget trace; stopping cannot alter the + # algorithm's accepted prefix. No StopState is used for this oracle. + scores = [] + for end in range(1, 7): + prefix = recipe(train, valid, context=context, rounds=end, bins=4) + scores.append(loss(valid, prefix.state.validation_raw)) + stale, best, end = 0, baseline, 6 + for j, score in enumerate(scores, 1): + stale = 0 if score < best else stale + 1 + best = min(best, score) + if stale == patience: + end = j + break + expected[context.run_id] = recipe(train, valid, context=context, rounds=end, bins=4) + expected_rounds[context.run_id] = end + specs.append( + RunSpec( + context, + train, + valid, + recipe, + {"rounds": 6, "bins": 4, "patience": patience}, + prepared, + ) + ) + if count > 1: + assert len(set(expected_rounds.values())) > 1 + bad = RunSpec( + RunContext("bad", 1), + p, + p, + recipes.squared, + {"rounds": 6, "bins": 4, "patience": 0}, + prepared, + ) + + def forbidden(*args, **kwargs): + raise AssertionError("shared preparation must not refit") + + monkeypatch.setattr(Binning, "fit", forbidden) + first = run_many([bad, *specs]) + assert first[0].error_type == "ValueError" and first[0].result is None + groups = [ + first[1:], + run_many(reversed(specs)), + [out for group in (specs[::2], specs[1::2]) for out in run_many(group)], + run_many(specs), + ] + stops = {out.run_id: out.result.stop for out in first[1:]} + for outcomes in groups: + for out in outcomes: + assert out.error_type is None + ref = expected[out.run_id] + assert out.result.state.identity == ref.state.identity + assert out.result.stop.completed_rounds == expected_rounds[out.run_id] + assert out.result.stop == stops[out.run_id] + np.testing.assert_array_equal(out.result.state.validation_raw, ref.state.validation_raw) + assert out.result.state.best_model.identity == ref.state.best_model.identity + for left, right in zip(first[1:], first[2:], strict=False): + assert not np.shares_memory(left.result.state.train_raw, right.result.state.train_raw) + + +def test_nonfinite_validation_failure_is_retained_and_isolated(): + p = problem() + bad_valid = replace(p, target=np.full((6, 1), 1e200)) + jobs = [ + RunSpec( + RunContext("bad-metric", 1), p, bad_valid, recipes.squared, {"rounds": 5, "patience": 1} + ), + RunSpec(RunContext("good-metric", 1), p, p, recipes.squared, {"rounds": 2, "patience": 1}), + ] + with np.errstate(over="raise", invalid="raise"): + outcomes = run_many(jobs) + assert outcomes[0].error_type == "FloatingPointError" and outcomes[0].result is None + assert outcomes[1].error_type is None and outcomes[1].result.stop.reason == "budget" diff --git a/tests/v1/test_public_tree.py b/tests/v1/test_public_tree.py new file mode 100644 index 0000000..d1c2d31 --- /dev/null +++ b/tests/v1/test_public_tree.py @@ -0,0 +1,231 @@ +"""Depthwise trees checked against independent exhaustive original-row growth.""" + +import json +import os +import subprocess +import sys +from functools import partial + +import numpy as np +import pytest + +from openboost import NumericData, Problem +from openboost.binning import Binning +from openboost.ops import feasible, newton_leaf, score +from openboost.stats import newton +from openboost.tree import Tree, depthwise +from tests.v1.reference.tree import fit_tree + + +def fixture(): + x = NumericData( + [[0, 2], [1, 0], [2, 1], [3, 3], [4, np.nan], [np.nan, 1], [6, 2], [7, 0]], + np.arange(8) + 40, + ("a", "b"), + ) + p = Problem(x, np.zeros((8, 1)), x.row_ids, weight=[1, 0, 2, 1, 3, 1, 1, 2]) + g, h = np.array([-8, -3, -2, -1, 1, 3, 5, 8.0]), np.ones(8) + b = Binning.fit(x, bins=8).transform(x) + return p, b, g, h + + +@pytest.mark.parametrize("depth,leaves", [(0, 1), (1, 8), (2, 3), (3, 5), (8, None)]) +def test_topology_and_inference_match_exhaustive_reference(depth, leaves): + p, b, g, h = fixture() + actual = depthwise(b, newton(p, g, h), max_depth=depth, max_leaves=leaves) + ref = fit_tree( + np.where(b.missing.T, np.nan, b.codes.T), + g, + h, + weight=p.weight, + max_depth=depth, + max_leaves=leaves, + ) + assert len(actual.value) == len(ref.nodes) + for i, node in enumerate(ref.nodes): + assert actual.value[i] == pytest.approx(node.value) + assert (actual.left[i], actual.right[i]) == (node.left, node.right) + if node.condition is None: + assert actual.feature[i] == -1 + else: + assert (actual.feature[i], actual.threshold[i], actual.missing_left[i]) == ( + node.condition.feature, + node.condition.threshold, + node.condition.missing_left, + ) + unseen = NumericData( + [[-100, 0], [100, 2], [np.nan, np.nan], [3.5, 1]], [1, 2, 3, 4], ("a", "b") + ) + encoded = b.binning.transform(unseen) + np.testing.assert_allclose( + actual.predict(unseen)[:, 0], + ref.predict(np.where(encoded.missing.T, np.nan, encoded.codes.T)), + ) + with pytest.raises(ValueError): + actual.left.flags.writeable = True + + +def test_custom_callbacks_change_tree_and_leaf_values(): + p, b, g, h = fixture() + fields = newton(p, g, h).add_independent("mass", np.ones(8)) + calls = {"score": 0, "legal": 0, "leaf": 0} + + def scoring(c): + calls["score"] += 1 + return score(c) if c.feature == 1 else 0 + + def legality(c): + calls["legal"] += 1 + return feasible(c, min_information={"mass": 2}) + + def leaf(total, names): + calls["leaf"] += 1 + return 2 * newton_leaf(total, names) + + custom = depthwise(b, fields, scoring=scoring, legality=legality, leaf=leaf) + assert all(calls.values()) and calls["leaf"] == len(custom.value) + assert set(custom.feature) == {-1, 1} + assert custom.identity != depthwise(b, fields).identity + expected = fit_tree( + np.where(b.missing.T, np.nan, b.codes.T), + g, + h, + weight=p.weight, + information=np.ones((8, 1)), + min_information=2, + ) + constrained = depthwise(b, fields, legality=partial(feasible, min_information={"mass": 2})) + np.testing.assert_allclose( + constrained.predict(p.data)[:, 0], + expected.predict(np.where(b.missing.T, np.nan, b.codes.T)), + ) + baseline = depthwise(b, fields, scoring=scoring, legality=legality) + np.testing.assert_allclose(custom.predict(p.data), 2 * baseline.predict(p.data)) + + +def test_tree_roundtrip_in_fresh_process(tmp_path): + p, b, g, h = fixture() + tree = depthwise(b, newton(p, g, h)) + path = tmp_path / "tree.json" + tree.save(path) + restored = Tree.load(path) + assert restored.identity == tree.identity + data = NumericData([[-100, 0], [100, 2], [np.nan, np.nan]], [1, 2, 3], ("a", "b")) + np.testing.assert_array_equal(restored.predict(data), tree.predict(data)) + code = """import json, sys +import numpy as np +from openboost import NumericData +from openboost.tree import Tree +x = NumericData([[-100, 0], [100, 2], [np.nan, np.nan]], [1, 2, 3], ("a", "b")) +print(json.dumps(Tree.load(sys.argv[1]).predict(x).tolist())) +""" + output = subprocess.check_output( + [sys.executable, "-c", code, str(path)], env=os.environ, text=True + ) + np.testing.assert_array_equal(json.loads(output), tree.predict(data)) + wrong = NumericData(data.values, data.row_ids, ("b", "a")) + with pytest.raises(ValueError): + tree.predict(wrong) + + +@pytest.mark.parametrize( + "corruption", + [ + "cycle", + "shared", + "outside", + "fraction", + "schema", + "nan", + "leaf", + "cuts", + "route", + "unreachable", + "unknown", + ], +) +def test_corrupt_artifacts_rejected(tmp_path, corruption): + p, b, g, h = fixture() + record = depthwise(b, newton(p, g, h)).record() + if corruption == "cycle": + record["left"][0] = 0 + elif corruption == "shared": + record["right"][0] = record["left"][0] + elif corruption == "outside": + record["left"][0] = 2**31 + elif corruption == "fraction": + record["left"][0] = 1.5 + elif corruption == "schema": + record["feature"][0] = 2 + elif corruption == "nan": + record["value"][0] = float("nan") + elif corruption == "leaf": + record["left"][-1] = 0 + elif corruption == "cuts": + record["cuts"][0] = [2, 1] + elif corruption == "route": + record["missing_left"][0] = 1 + elif corruption == "unreachable": + for name, value in zip( + ("feature", "threshold", "missing_left", "left", "right", "value"), + (-1, -1, False, -1, -1, 0), + strict=True, + ): + record[name].append(value) + else: + record["extra"] = 1 + path = tmp_path / "bad.json" + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + Tree.load(path) + + +@pytest.mark.parametrize( + "kwargs", + [ + {"max_depth": -1}, + {"max_depth": True}, + {"max_leaves": 0}, + {"max_leaves": 2**31}, + {"leaf": lambda *_: np.nan}, + {"scoring": lambda _: np.nan}, + ], +) +def test_bad_configuration_and_callbacks_rejected(kwargs): + p, b, g, h = fixture() + with pytest.raises(ValueError): + depthwise(b, newton(p, g, h), **kwargs) + + +def test_score_once_and_empty_child_guard(): + p, b, g, h = fixture() + seen = set() + + def scoring(c): + key = (c.rows_identity, c.key) + assert key not in seen + seen.add(key) + return score(c) + + depthwise(b, newton(p, g, h), scoring=scoring, max_leaves=3) + assert seen + constant = NumericData([[1], [1]], [1, 2], ("x",)) + problem = Problem(constant, [[0], [0]], constant.row_ids) + with pytest.raises(ValueError, match="empty child"): + depthwise( + Binning.fit(constant).transform(constant), + newton(problem, [-1, 1], [1, 1]), + legality=lambda _: True, + scoring=lambda _: 1, + ) + + +def test_duplicate_fields_and_root_only_artifact(tmp_path): + p, b, g, h = fixture() + tree = depthwise(b, newton(p, g, h), max_depth=0) + path = tmp_path / "tree.json" + tree.save(path) + assert Tree.load(path).identity == tree.identity + path.write_text(path.read_text().replace('"format":', '"format": "duplicate", "format":')) + with pytest.raises(ValueError, match="duplicate"): + Tree.load(path) diff --git a/tests/v1/test_public_tweedie.py b/tests/v1/test_public_tweedie.py new file mode 100644 index 0000000..70647f0 --- /dev/null +++ b/tests/v1/test_public_tweedie.py @@ -0,0 +1,148 @@ +"""Fixed-power Tweedie mean geometry and round composition.""" + +from dataclasses import replace + +import numpy as np +import pytest + +from openboost import MixedData, Problem, RunContext +from openboost.objectives import Tweedie +from openboost.outputs import positive_mean +from openboost.recipes import tweedie +from tests.v1.reference.mixed import Transformer, grow +from tests.v1.reference.positive import tweedie as reference + + +def fixture(): + x = MixedData( + [[0, "a"], [1, "b"], [2, None], [3, "a"], [4, "c"], [None, "b"]], + [0, 1, 2, 3, 4, 5], + ("x", "c"), + ("numeric", "categorical"), + ) + return Problem( + x, + [[0], [0.5], [4], [2], [0], [8]], + x.row_ids, + weight=[0.2, 2, 3, 0, 0.5, 1], + offset=np.arange(6.0)[:, None] / 10, + ) + + +@pytest.mark.parametrize("power", [1.1, 1.5, 1.9]) +def test_geometry_intercept_and_three_rounds(power): + p = fixture() + obj = Tweedie(power) + w, y, offset = p.weight, p.target[:, 0], p.offset[:, 0] + base = np.log( + np.sum(w * y * np.exp((1 - power) * offset)) / np.sum(w * np.exp((2 - power) * offset)) + ) + np.testing.assert_allclose(obj.base(p), [base]) + raw = np.full((6, 1), base) + np.testing.assert_allclose(np.dot(w, obj.geometry(p, raw)[1]), 0, atol=1e-12) + fit = tweedie(p, p, context=RunContext("tweedie", 7), power=power, rounds=3, bins=4) + transform = Transformer.fit( + p.data.values, names=p.data.feature_names, kinds=p.data.feature_kinds, bins=4 + ) + for step in fit.steps: + loss, g, h = reference(p.with_offset(raw)[:, 0], y, power=power, weight=w) + np.testing.assert_allclose(step.gradient, g) + np.testing.assert_allclose(step.curvature, h) + np.testing.assert_allclose(step.loss_before, loss) + tree = grow(p.data.values, g[:, None], h[:, None], transform, weight=w) + np.testing.assert_allclose(step.raw_before, raw) + raw += 0.1 * tree.predict(p.data.values) + np.testing.assert_allclose(step.raw_after, raw) + assert fit.state.version == 3 + + +def test_derivatives_include_zero_targets(): + p = fixture() + obj = Tweedie() + raw = np.arange(6.0)[:, None] / 4 + loss, g, h = obj.geometry(p, raw) + epsilon = 1e-4 + for i in [0, 1, 4]: + delta = np.zeros_like(raw) + delta[i] = epsilon + plus, minus = obj.loss(p, raw + delta), obj.loss(p, raw - delta) + np.testing.assert_allclose( + (plus - minus) / (2 * epsilon), g[i] * p.weight[i] / p.weight.sum(), atol=1e-8 + ) + np.testing.assert_allclose( + (plus + minus - 2 * loss) / epsilon**2, h[i] * p.weight[i] / p.weight.sum(), atol=1e-6 + ) + + +def test_all_zero_initializer_and_annualized_units(): + p = replace(fixture(), offset=np.zeros((6, 1))) + zero = replace(p, target=np.zeros((6, 1))) + assert Tweedie(minimum_mean=1e-4).base(zero)[0] == np.log(1e-4) + fit = tweedie(zero, zero, context=RunContext("zero", 1), minimum_mean=1e-4) + assert np.isfinite(fit.state.train_raw).all() + # Period amounts become annualized targets; exposure enters weights only. + exposure = np.array([0.5, 1, 2, 3, 0.2, 1]) + total = np.array([0, 2, 4, 6, 0, 3]) + annual = replace(p, target=(total / exposure)[:, None], weight=exposure) + raw = np.zeros((6, 1)) + got = Tweedie().geometry(annual, raw) + expected = reference(raw[:, 0], total / exposure, weight=exposure) + for a, b in zip(got, expected, strict=True): + np.testing.assert_allclose(a, b) + np.testing.assert_allclose(exposure * positive_mean(raw), exposure) + + +@pytest.mark.parametrize("power", [1, 2, np.nan]) +def test_invalid_power(power): + with pytest.raises(ValueError): + Tweedie(power) + + +def test_invalid_support_structure_and_overflow(): + p = fixture() + for invalid in ( + replace(p, target=np.full((6, 1), -1)), + replace(p, structure={"exposure": np.ones((6, 1))}), + ): + with pytest.raises(ValueError): + tweedie(invalid, invalid, context=RunContext("invalid", 1)) + with pytest.raises((ValueError, FloatingPointError)): + Tweedie().geometry(p, np.full((6, 1), 2000)) + with pytest.raises(ValueError): + Tweedie(minimum_mean=0) + + +def test_rejected_update_and_fresh_process_mean(tmp_path): + import json + import subprocess + import sys + + from openboost.artifacts import Model + from openboost.tree import depthwise + + p = fixture() + + def zero(data, fields): + return depthwise(data, fields, max_depth=0, leaf=lambda *_: 0) + + rejected = tweedie( + p, p, context=RunContext("reject", 1), learner=zero, step="backtracking", rounds=2 + ) + assert rejected.state.version == 0 + assert all(not s.accepted and s.raw_before is s.raw_after for s in rejected.steps) + model = tweedie(p, p, context=RunContext("persist", 1)).state.model + path = tmp_path / "model.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import MixedData +from openboost.artifacts import Model +from openboost.outputs import positive_mean +x = MixedData([[1,'unknown'], [None,None]], [10,11], ('x','c'), ('numeric','categorical')) +print(json.dumps(positive_mean(Model.load(sys.argv[1]).predict(x, offset=[[0.1],[0.2]])).tolist())) +""" + x = MixedData( + [[1, "unknown"], [None, None]], [10, 11], p.data.feature_names, p.data.feature_kinds + ) + got = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + np.testing.assert_array_equal(got, positive_mean(model.predict(x, offset=[[0.1], [0.2]]))) diff --git a/tests/v1/test_public_vector_multiclass.py b/tests/v1/test_public_vector_multiclass.py new file mode 100644 index 0000000..02963b9 --- /dev/null +++ b/tests/v1/test_public_vector_multiclass.py @@ -0,0 +1,183 @@ +"""Joint vector topology and multiclass conformance using independent oracles.""" + +import json +import subprocess +import sys + +import numpy as np +import pytest + +from openboost import ClassSchema, MixedData, Problem, RunContext +from openboost.artifacts import Model, TreeTerm +from openboost.binning import Binning +from openboost.objectives import Multiclass +from openboost.ops import vector_feasible, vector_leaf, vector_score +from openboost.recipes import multiclass +from openboost.stats import vector_newton +from openboost.tree import Tree, best_first, depthwise, symmetric +from tests.v1.reference.classification import softmax +from tests.v1.reference.mixed import Transformer, grow + + +def fixture(): + x = MixedData( + [ + [0, "a"], + [1, "b"], + [2, "c"], + [3, None], + [4, "a"], + [None, "b"], + [1, "c"], + [2, "a"], + [3, "b"], + ], + np.arange(9), + ("x", "c"), + ("numeric", "categorical"), + ) + schema = ClassSchema.fit(["a", "b", "c"]) + p = Problem( + x, + schema.encode(["a", "b", "c", "c", "a", "b", "c", "a", "b"]), + x.row_ids, + weight=[1, 0, 2, 1, 3, 1, 2, 1, 1], + raw_width=3, + classes=schema, + ) + ref = Transformer.fit(x.values, names=x.feature_names, kinds=x.feature_kinds, bins=4) + return p, Binning.fit(x, bins=4).transform(x), ref + + +@pytest.mark.parametrize("policy", [depthwise, best_first, symmetric]) +@pytest.mark.parametrize("projected", [False, True]) +def test_vector_tree_and_full_leaves_match_independent_oracle(policy, projected): + p, b, ref_transform = fixture() + rng = np.random.default_rng(27) + g, h = rng.normal(size=(9, 3)), rng.uniform(0.5, 2, (9, 3)) + projection = np.array([[1.0], [0], [0]]) if projected else np.eye(3) + fields = vector_newton(p, g @ projection, h @ (projection**2)) + leaves = vector_newton(p, g, h) + tree = policy( + b, + fields, + max_depth=3, + scoring=vector_score, + legality=vector_feasible, + leaf=vector_leaf, + leaf_fields=leaves, + ) + if projected: + full = policy( + b, leaves, max_depth=3, scoring=vector_score, legality=vector_feasible, leaf=vector_leaf + ) + assert tree.identity != full.identity + expected = grow( + p.data.values, + g, + h, + ref_transform, + weight=p.weight, + projection=projection, + policy=policy.__name__, + max_depth=3, + ) + assert tree.output_width == 3 and len(tree.value) == len(expected.nodes) + for i, node in enumerate(expected.nodes): + np.testing.assert_allclose(tree.value[i], node.value) + assert (tree.left[i], tree.right[i]) == (node.left, node.right) + if node.condition: + assert (tree.feature[i], tree.threshold[i], tree.missing_left[i]) == node.condition + np.testing.assert_allclose(tree.predict(p.data), expected.predict(p.data.values)) + # Learner width L can differ from model raw width K. + term = TreeTerm(tree, [[1, 0], [0, 1], [1, -1]], 0.5) + model = Model(p.data.feature_names, [0, 1], (term,)) + np.testing.assert_allclose( + model.predict(p.data), [0, 1] + 0.5 * tree.predict(p.data) @ term.mapping + ) + + +def test_three_round_multiclass_geometry_and_joint_tree_oracle(): + p, b, transform = fixture() + fit = multiclass(p, p, context=RunContext("softmax", 1), rounds=3, bins=4) + raw = np.zeros((9, 3)) + for actual in fit.steps: + loss, _prob, g, exact, bound = softmax(raw, p.target[:, 0], weight=p.weight) + np.testing.assert_allclose(actual.gradient, g) + np.testing.assert_allclose(actual.diagonal_bound, bound) + assert np.linalg.eigvalsh(np.array([np.diag(row) for row in bound]) - exact).min() > -1e-12 + tree = grow(p.data.values, g, bound, transform, weight=p.weight) + np.testing.assert_allclose(actual.raw_before, raw) + raw += 0.1 * tree.predict(p.data.values) + np.testing.assert_allclose(actual.raw_after, raw) + np.testing.assert_allclose( + [actual.loss_before, actual.loss_after], + [loss, softmax(raw, p.target[:, 0], weight=p.weight)[0]], + ) + assert fit.state.version == 3 and len(fit.state.model.terms) == 3 + assert all(t.learner.output_width == 3 for t in fit.state.model.terms) + np.testing.assert_allclose( + fit.state.model.predict_proba(p.data), softmax(raw, p.target[:, 0])[1] + ) + + +def test_vector_classifier_fresh_process_and_corrupt_payload(tmp_path): + p, _b, _ref = fixture() + model = multiclass(p, p, context=RunContext("persist", 1), rounds=2).state.model + path = tmp_path / "model.json" + model.save(path) + assert Model.load(path).identity == model.identity + code = """import json, sys +from openboost import MixedData +from openboost.artifacts import Model +x = MixedData([[-10,'a'],[100,'unknown'],[None,None]], [10,11,12], ('x','c'), ('numeric','categorical')) +m = Model.load(sys.argv[1]) +print(json.dumps([m.predict_proba(x).tolist(), m.predict_label(x)])) +""" + output = json.loads(subprocess.check_output([sys.executable, "-c", code, str(path)], text=True)) + x = MixedData( + [[-10, "a"], [100, "unknown"], [None, None]], + [10, 11, 12], + ("x", "c"), + ("numeric", "categorical"), + ) + np.testing.assert_array_equal(output[0], model.predict_proba(x)) + assert tuple(output[1]) == model.predict_label(x) + for corrupt in ("width", "nan", "mapping"): + record = model.record() + if corrupt == "mapping": + record["terms"][0]["mapping"] = [[1, 0, 0]] + else: + record["terms"][0]["learner"]["value"][-1] = ( + [1] if corrupt == "width" else [float("nan")] * 3 + ) + path.write_text(json.dumps(record)) + with pytest.raises(ValueError): + Model.load(path) + + +def test_offsets_joint_rejection_and_foreign_leaf_fields(): + p, b, _ref = fixture() + from dataclasses import replace + + shifted = replace(p, offset=np.arange(27).reshape(9, 3) / 10) + raw = np.zeros((9, 3)) + got = Multiclass.geometry(shifted, raw) + ref = softmax(shifted.with_offset(raw), p.target[:, 0], weight=p.weight) + for a, expected in zip(got, (ref[0], ref[2], ref[4]), strict=True): + np.testing.assert_allclose(a, expected) + + def zero(data, fields): + return depthwise(data, fields, max_depth=0, leaf=lambda *_: [0, 0, 0]) + + fit = multiclass( + p, p, context=RunContext("reject", 1), learner=zero, rounds=2, step="backtracking" + ) + assert fit.state.version == 0 and not fit.state.model.terms + assert all(not item.accepted and item.raw_before is item.raw_after for item in fit.steps) + fields = vector_newton(p, np.ones((9, 3)), np.ones((9, 3))) + other = vector_newton(shifted, np.ones((9, 3)), np.ones((9, 3))) + with pytest.raises(ValueError, match="same problem"): + depthwise(b, fields, leaf_fields=other, leaf=vector_leaf) + with pytest.raises(ValueError): + Tree.from_record({"format": "openboost-tree-v2"}) diff --git a/tests/v1/test_quality_artifacts.py b/tests/v1/test_quality_artifacts.py new file mode 100644 index 0000000..6f6821c --- /dev/null +++ b/tests/v1/test_quality_artifacts.py @@ -0,0 +1,243 @@ +import hashlib + +import numpy as np +from benchmarks.v1.quality_report import report + + +def test_recomputes_scores_and_rejects_row_permutation(tmp_path): + cells = [] + for fold in range(5): + truth = tmp_path / f"truth{fold}.npz" + pred = tmp_path / f"pred{fold}.npz" + np.savez(truth, row_ids=[1, 2], y=[0.0, 1.0]) + np.savez(pred, row_ids=[1, 2], prediction=[0.0, 1.0]) + + def entry(p): + return {"path": p.name, "sha256": hashlib.sha256(p.read_bytes()).hexdigest()} + + cells.append( + { + "application": "A1", + "fold": fold, + "kind": "loss", + "primary": ["rmse"], + "truth": entry(truth), + "candidate": entry(pred), + "baseline": entry(pred), + } + ) + manifest = {"schema": "openboost-quality-pairs-v1", "cells": cells} + r = report(manifest, tmp_path) + assert r["comparisons"]["A1"]["pass"] + assert not r["E3_pass"] # A2–A13 are absent. + np.savez(tmp_path / "pred0.npz", row_ids=[2, 1], prediction=[0.0, 1.0]) + # Even with an updated byte hash, row misalignment must fail. + cells[0]["candidate"]["sha256"] = hashlib.sha256( + (tmp_path / "pred0.npz").read_bytes() + ).hexdigest() + assert report(manifest, tmp_path)["errors"] + + +def test_no_declared_scores_can_replace_arrays(tmp_path): + r = report({"schema": "openboost-quality-pairs-v1", "cells": []}, tmp_path) + assert not r["E3_pass"] + + +def test_survival_auxiliary_is_hashed_reported_and_support_checked(tmp_path): + import json + + from benchmarks.v1.preprocessing import censoring_support + + def entry(path): + return {"path": path.name, "sha256": hashlib.sha256(path.read_bytes()).hexdigest()} + + truth = tmp_path / "survival.npz" + pred = tmp_path / "survival-pred.npz" + support_path = tmp_path / "censoring.json" + np.savez(truth, row_ids=[1, 2, 3], y=[1.0, 2.0, 4.0], event=[1, 0, 1]) + np.savez( + pred, row_ids=[1, 2, 3], prediction=np.column_stack([np.full(3, np.log(2)), np.ones(3)]) + ) + support = censoring_support([1, 2, 3, 4], [1, 0, 1, 1]) + support["grid"] = [2.0] + support_path.write_text(json.dumps(support)) + cells = [ + dict( + application="A10", + fold=f, + kind="nll", + primary=["nll"], + truth=entry(truth), + candidate=entry(pred), + baseline=entry(pred), + ) + for f in range(5) + ] + manifest = dict(schema="openboost-quality-pairs-v1", cells=cells) + missing = report(manifest, tmp_path) + assert len(missing["auxiliary_missing"]) == 5 + for cell in cells: + cell["auxiliary"] = {"censoring": entry(support_path)} + result = report(manifest, tmp_path) + assert not result["errors"] and not result["auxiliary_missing"] + assert result["comparisons"]["A10"]["fold_metrics"]["0"]["candidate"]["contributing_rows"] == [ + 2 + ] + assert result["comparisons"]["A10"]["metrics"]["nll"]["paired_summary"]["percentile95"] == [ + 0.0, + 0.0, + ] + assert not result["E3_pass"] + support["grid"] = [4.0] + support_path.write_text(json.dumps(support)) + for cell in cells: + cell["auxiliary"]["censoring"] = entry(support_path) + assert report(manifest, tmp_path)["errors"] + + +def test_structural_auxiliary_preserves_row_identity(tmp_path): + def entry(path): + return {"path": path.name, "sha256": hashlib.sha256(path.read_bytes()).hexdigest()} + + truth, pred, support = [tmp_path / n for n in ["y.npz", "p.npz", "age.npz"]] + np.savez(truth, row_ids=[1, 2], y=[0.0, 0.0]) + np.savez(pred, row_ids=[1, 2], prediction=[1.0, 1.0]) + np.savez(support, row_ids=[1, 2], age=[1.0, 3.0], train_min=1.0, train_max=2.0) + cells = [ + dict( + application="A12", + fold=f, + kind="loss", + primary=["rmse"], + truth=entry(truth), + candidate=entry(pred), + baseline=entry(pred), + auxiliary={"structure": entry(support)}, + ) + for f in range(5) + ] + manifest = dict(schema="openboost-quality-pairs-v1", cells=cells) + result = report(manifest, tmp_path) + assert not result["errors"] and not result["auxiliary_missing"] + assert result["comparisons"]["A12"]["fold_metrics"]["0"]["candidate"]["structure_errors"][ + "above" + ] == {"rows": 1, "rmse": 1.0} + np.savez(support, row_ids=[2, 1], age=[1.0, 3.0], train_min=1.0, train_max=2.0) + for cell in cells: + cell["auxiliary"]["structure"] = entry(support) + assert report(manifest, tmp_path)["errors"] + + +def entry(path): + return {"path": path.name, "sha256": hashlib.sha256(path.read_bytes()).hexdigest()} + + +def multioutput_fixture(root): + import json + + from benchmarks.v1.preprocessing import fit_target_scale + + cells = [] + for fold in range(5): + + def save(name, fold=fold, **values): + path = root / f"{fold}-{name}.npz" + np.savez(path, **values) + return entry(path) + + target = np.array([[-1.0, -100.0, 7.0], [1.0, 100.0, 7.0]]) + scale_path = root / f"{fold}-scale.json" + scale_path.write_text(json.dumps(fit_target_scale(target))) + cells.append( + dict( + application="A6", + fold=fold, + kind="loss", + primary=["rmse_0", "rmse_1", "rmse_2", "standardized_rmse"], + truth=save("truth", row_ids=[2, 3], y=np.zeros((2, 3))), + candidate=save( + "candidate", row_ids=[2, 3], prediction=np.tile([1.0, 100.0, 0.0], (2, 1)) + ), + baseline=save( + "baseline", row_ids=[2, 3], prediction=np.tile([1.0, 100.0, 0.0], (2, 1)) + ), + auxiliary=dict( + train_rows=save("rows", row_ids=[0, 1]), + train_targets=save("targets", row_ids=[0, 1], y=target), + target_scale=entry(scale_path), + ), + ) + ) + return dict(schema="openboost-quality-pairs-v1", cells=cells) + + +def test_a6_reports_standardized_average(tmp_path): + manifest = multioutput_fixture(tmp_path) + result = report(manifest, tmp_path) + assert not result["errors"] + actual = result["comparisons"]["A6"] + assert actual["pass"] + assert actual["fold_metrics"]["0"]["candidate"]["standardized_rmse"] == 2 / 3 + assert set(actual["metrics"]) == {"rmse_0", "rmse_1", "rmse_2", "standardized_rmse"} + assert not result["E3_pass"] + + +def test_a6_average_cannot_hide_target_regression(tmp_path): + manifest = multioutput_fixture(tmp_path) + for cell in manifest["cells"]: + path = tmp_path / cell["candidate"]["path"] + np.savez(path, row_ids=[2, 3], prediction=np.tile([1.2, 0.0, 0.0], (2, 1))) + cell["candidate"] = entry(path) + result = report(manifest, tmp_path) + assert not result["errors"] + comparison = result["comparisons"]["A6"] + assert comparison["metrics"]["standardized_rmse"]["pass"] + assert not comparison["metrics"]["rmse_0"]["pass"] + assert not comparison["pass"] + + +def test_a6_bad_scale_support_is_rejected(tmp_path): + import copy + import json + + manifest = multioutput_fixture(tmp_path) + original = copy.deepcopy(manifest) + manifest["cells"][0].pop("auxiliary") + assert report(manifest, tmp_path)["errors"] + manifest = copy.deepcopy(original) + manifest["cells"][0]["primary"].remove("rmse_0") + assert report(manifest, tmp_path)["errors"] + manifest = copy.deepcopy(original) + path = tmp_path / manifest["cells"][0]["auxiliary"]["target_scale"]["path"] + record = json.loads(path.read_text()) + record["std"][1] = 1.0 + path.write_text(json.dumps(record)) + manifest["cells"][0]["auxiliary"]["target_scale"] = entry(path) + assert report(manifest, tmp_path)["errors"] + + +def test_a6_training_overlap_and_target_permutation_fail(tmp_path): + manifest = multioutput_fixture(tmp_path) + support = manifest["cells"][0]["auxiliary"] + path = tmp_path / support["train_targets"]["path"] + np.savez(path, row_ids=[1, 0], y=[[-1.0, -100.0, 7.0], [1.0, 100.0, 7.0]]) + support["train_targets"] = entry(path) + assert report(manifest, tmp_path)["errors"] + row_path = tmp_path / support["train_rows"]["path"] + np.savez(row_path, row_ids=[1, 2]) + np.savez(path, row_ids=[1, 2], y=[[-1.0, -100.0, 7.0], [1.0, 100.0, 7.0]]) + support.update(train_rows=entry(row_path), train_targets=entry(path)) + assert report(manifest, tmp_path)["errors"] + + +def test_a6_scale_cannot_use_evaluation_targets(tmp_path): + import json + + from benchmarks.v1.preprocessing import fit_target_scale + + manifest = multioutput_fixture(tmp_path) + cell = manifest["cells"][0] + path = tmp_path / cell["auxiliary"]["target_scale"]["path"] + path.write_text(json.dumps(fit_target_scale(np.zeros((2, 3))))) + cell["auxiliary"]["target_scale"] = entry(path) + assert report(manifest, tmp_path)["errors"] diff --git a/tests/v1/test_quality_evaluation.py b/tests/v1/test_quality_evaluation.py new file mode 100644 index 0000000..96f252a --- /dev/null +++ b/tests/v1/test_quality_evaluation.py @@ -0,0 +1,51 @@ +"""Quality cannot pass by missing folds, hiding targets, or choosing on test.""" + +import numpy as np +import pytest +from benchmarks.v1.quality import compare_folds, metrics, select_validation + + +def test_quality_boundaries_and_missing_folds(): + assert compare_folds([1] * 5, [1] * 5, "loss")["pass"] + assert not compare_folds([1, 1, 1, 1, 1.16], [1] * 5, "loss")["pass"] + assert not compare_folds([1] * 4, [1] * 4, "loss")["pass"] + assert not compare_folds([float("nan")] * 5, [1] * 5, "loss")["pass"] + assert compare_folds([-2] * 5, [-2.01] * 5, "nll")["pass"] + assert not compare_folds([0.1] * 5, [0] * 5, "loss")["pass"] + + +def test_selection_never_reads_test_and_fails_incomplete_search(): + records = [{"id": str(i), "validation": float(i), "status": "pass"} for i in range(16)] + assert select_validation(records) == "0" + records[-1]["status"] = "timeout" + with pytest.raises(ValueError): + select_validation(records) + + +def test_multioutput_cannot_average_away_failed_target(): + m = metrics("A6", [[0, 0], [2, 4]], [[0, 0], [2, 0]]) + assert m["rmse_0"] == 0 and m["rmse_1"] == pytest.approx(np.sqrt(8)) + + +def test_proper_scores_and_exposure_weight(): + assert metrics("A7", [0, 2], [1, 2])["poisson_deviance"] == pytest.approx(1) + assert metrics("A8", [1, 2], [1, 2])["gamma_deviance"] == 0 + assert metrics("A11", [0], [[0, 1]])["nll"] == pytest.approx(0.5 * np.log(2 * np.pi)) + assert metrics("A2", [0, 1], [0.5, 0.5])["logloss"] == pytest.approx(np.log(2)) + + +def test_survival_censor_is_not_an_event(): + event = metrics("A10", [1], [[0, 1]], event=[1])["nll"] + censored = metrics("A10", [1], [[0, 1]], event=[0])["nll"] + assert event == pytest.approx(0.5 * np.log(2 * np.pi)) + assert censored == pytest.approx(np.log(2)) + assert np.isfinite(metrics("A10", [np.exp(40)], [[0, 1]], event=[0])["nll"]) + + +@pytest.mark.parametrize( + "task,y,p", + [("A2", [1], [2]), ("A3", [0], [[0.2, 0.2]]), ("A8", [0], [1]), ("A11", [0], [[0, -1]])], +) +def test_invalid_predictions_or_targets_fail(task, y, p): + with pytest.raises(ValueError): + metrics(task, y, p) diff --git a/tests/v1/test_ranking_worker.py b/tests/v1/test_ranking_worker.py new file mode 100644 index 0000000..3b07c10 --- /dev/null +++ b/tests/v1/test_ranking_worker.py @@ -0,0 +1,34 @@ +import numpy as np +import pytest +from benchmarks.v1.ranking import groups, validate + + +def test_group_weights_have_explicit_identity_and_sizes(): + ids, sizes, weights = groups(np.array([8, 8, 3, 3, 3]), 5, [2, 0.5]) + assert ids.tolist() == [8, 3] + assert sizes.tolist() == [2, 3] + assert weights.tolist() == [2, 0.5] + + +@pytest.mark.parametrize( + "query,weight", [([1, 2, 1], None), ([1, 1, 2], [1, 1, 1]), ([1, 1, 2], [0, 0])] +) +def test_fragmented_groups_and_invalid_query_weights_fail(query, weight): + with pytest.raises(ValueError): + groups(query, 3, weight) + + +def test_query_overlap_and_row_weights_are_rejected(): + a = dict( + x_train=np.ones((3, 2)), + x_validation=np.ones((2, 2)), + query_train=np.array([1, 1, 2]), + query_validation=np.array([2, 2]), + y_train=np.array([0, 1, 2]), + ) + with pytest.raises(ValueError, match="overlap"): + validate(a, None) + a["query_validation"] = np.array([3, 3]) + a["weight_train"] = np.ones(3) + with pytest.raises(ValueError, match="row weights"): + validate(a, None) diff --git a/tests/v1/test_reference_independence.py b/tests/v1/test_reference_independence.py new file mode 100644 index 0000000..c3c3623 --- /dev/null +++ b/tests/v1/test_reference_independence.py @@ -0,0 +1,73 @@ +"""Prove that importing and running reference code does not load production.""" + +import subprocess +import sys +from pathlib import Path + + +def test_reference_runs_with_production_imports_blocked(): + script = """ +import importlib.abc +import sys + +class BlockProduction(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname == "openboost" or fullname.startswith("openboost."): + raise AssertionError("reference tried to import production: " + fullname) + +sys.meta_path.insert(0, BlockProduction()) +sys.path.insert(0, sys.argv[1]) +from tests.v1.reference.data import NumericBinning, CategoryMap +from tests.v1.reference.classification import ClassMap, binary, softmax +assert NumericBinning.fit([0, 0, 0, 1], bins=2).cuts == (0.,) +assert CategoryMap.fit(['a', 'b']).transform(['new'])[1] == (True,) +assert ClassMap.fit(['b', 'a']).encode(['b']) == (1,) +assert binary([0], [1])[1][0] == -.5 +assert abs(softmax([[0, 0, 0]], [0])[1][0, 0] - 1/3) < 1e-15 +from tests.v1.reference.ranking import pairwise +from tests.v1.reference.quantile import weighted_quantile +from tests.v1.reference.vector import fit_vector_stump +assert pairwise([0, 0], [1, 0], [0, 0]).gradient[0] == -.5 +assert weighted_quantile([0, 2, 10], .5, [1, 3, 1]) == 2 +assert fit_vector_stump([[0], [1]], [[0], [0]], [[-1], [1]]).predict([[0]])[0, 0] == -.5 +from tests.v1.reference.positive import poisson, gamma, tweedie +from tests.v1.reference.survival import aft, normal_tail +assert poisson([0], [1], [1])[1][0] == 0 +assert gamma([0], [1])[1][0] == 0 +assert tweedie([0], [0])[1][0] == 1 +assert aft([0], [1], [1])[2][0] == 1 +assert normal_tail(40)[2] > .999 +from tests.v1.reference.coupled import normal, formula, directions +_, g, fisher = normal([[0, 0]], [1]) +assert directions(g, fisher)[0, 0] == 1 +assert formula([[0, 0]], [1], [1])[0] > 0 +from tests.v1.reference.runs import RunSpec, derive_seed, run_many +assert derive_seed(7, 'run-α', 2, 'tree', 'rows') == 6175955064790668999 +record = run_many([RunSpec('r', 0, 1, .1, 2, 2)], [[0], [1]], [[0], [1]], + {'r': ([[-1], [1]], [[-1], [1]])})['r'] +assert record.status == 'completed' and record.best_round == 1 +from tests.v1.reference.author import expectile_base, penalized_quantile, ordered_normal +assert abs(expectile_base([0, 2]) - 1.6) < 1e-12 +assert penalized_quantile([0, 2, 10], .5, [1, 3, 1], penalty=1, anchor=0) == 1.5 +assert ordered_normal([[0], [0]], [[0, 0], [0, 0]], [1, 3])[0].accepted +from tests.v1.reference.mixed import Transformer, grow +tr = Transformer.fit([['a'], ['b']], names=('c',), kinds=('categorical',)) +assert grow([['a'], ['b']], [[1], [-1]], [[1], [1]], tr).predict([['a']])[0, 0] == -.5 +from tests.v1.reference.integration import fit_positive +model, trace = fit_positive([[0], [1]], [0, 2], kind='poisson', exposure=[1, 1]) +assert len(model.terms) == len(trace) == 2 +from tests.v1.reference.tree import boost_squared +for policy in ("depthwise", "best_first", "symmetric"): + result = boost_squared([[0], [0], [1], [1]], [-2, -2, 2, 2], policy=policy) + assert abs(result.predict([[0]])[0] + 58/225) < 1e-12 +assert not any(n == "openboost" or n.startswith("openboost.") for n in sys.modules) +print("independent-reference-ok") +""" + result = subprocess.run( + [sys.executable, "-I", "-c", script, str(Path(__file__).resolve().parents[2])], + capture_output=True, + text=True, + timeout=30, + check=True, + ) + assert result.stdout.strip() == "independent-reference-ok" diff --git a/tests/v1/test_runs_reference.py b/tests/v1/test_runs_reference.py new file mode 100644 index 0000000..3681558 --- /dev/null +++ b/tests/v1/test_runs_reference.py @@ -0,0 +1,217 @@ +"""Data identity and run isolation checked with actual tiny tree fits.""" + +import numpy as np +import pytest + +from .reference.runs import ( + RunSpec, + bind_identity, + data_identity, + derive_seed, + run_many, + select_best, +) + + +def test_identity_is_typed_content_not_object_or_shape(): + arguments = dict( + row_ids=[10, 20], + values=[[0], [1]], + schema=["x"], + transformer={"bins": 2, "cuts": [0.5], "categories": []}, + ) + identity = data_identity(**arguments) + assert identity == data_identity(**arguments) + for change in ( + dict(values=[[0], [2]]), + dict(row_ids=[20, 10]), + dict(schema=["other"]), + dict(transformer={"bins": 3, "cuts": [0.5], "categories": []}), + ): + assert data_identity(**(arguments | change)) != identity + assert data_identity(**(arguments | {"values": [["1"], ["2"]]})) != data_identity( + **(arguments | {"values": [[1], [2]]}) + ) + assert data_identity( + **(arguments | {"transformer": {"categories": ["a", "b"]}}) + ) != data_identity(**(arguments | {"transformer": {"categories": ["b", "a"]}})) + values = np.array([[0.0], [1.0]]) + first = data_identity(**(arguments | {"values": values})) + values[0] = 99 + assert data_identity(**(arguments | {"values": values})) != first + + +def test_binding_rejects_misaligned_roles_and_includes_all_fields(): + prepared = data_identity([10, 20], [[0], [1]], ["x"], {"cuts": [0.5]}) + target = ([10, 20], [[1], [2]]) + first = bind_identity(prepared, [10, 20], target=target, weight=([10, 20], [1, 1])) + assert bind_identity(prepared, [10, 20], target=target, weight=([10, 20], [1, 2])) != first + assert bind_identity(prepared, [10, 20], target=target, offset=([10, 20], [[0], [1]])) != first + with pytest.raises(ValueError): + bind_identity(prepared, [10, 20], target=([20, 10], [[2], [1]])) + with pytest.raises(ValueError): + data_identity([10, 10], [[0], [1]], ["x"], {}) + + +def fixtures(): + bins = np.array([[0], [1], [2], [3]]) + y = np.array([[-2.0], [-1.0], [1.0], [2.0]]) + specs = [ + RunSpec("scalar", 7, 5, 0.2, 2, 3), + RunSpec("vector", 7, 3, 0.1, 3, 3), + RunSpec("flat", 7, 8, 0.0, 1, 4), + ] + problems = { + "scalar": (y, y), + "vector": (np.column_stack((y, 2 * y)), np.column_stack((y, 2 * y))), + "flat": (y, y), + } + return bins, specs, problems + + +def test_independent_sequential_reordered_and_regrouped_runs(): + bins, specs, problems = fixtures() + together = run_many(specs, bins, bins, problems) + reverse = run_many(list(reversed(specs)), bins, bins, problems) + separate = {s.run_id: run_many([s], bins, bins, problems)[s.run_id] for s in specs} + grouped = run_many(specs[::2], bins, bins, problems) | run_many( + specs[1::2], bins, bins, problems + ) + assert together == reverse == separate == grouped + assert together["flat"].status == "early_stopped" + assert together["flat"].rounds == 1 and together["flat"].best_round == 0 + assert together["scalar"].rounds == 5 and together["vector"].rounds == 3 + assert len(together["vector"].best_raw[0]) == 2 + + +def test_failure_isolation_retry_and_best_snapshot_reconstruction(): + bins, specs, problems = fixtures() + failed = RunSpec("broken", 7, 5, 0.2, 2, 3, fail_round=2) + problems["broken"] = problems["scalar"] + baseline = run_many(specs, bins, bins, problems) + result = run_many([failed, *specs], bins, bins, problems) + assert result["broken"].status == "failed" and "round 2" in result["broken"].error + assert {k: result[k] for k in baseline} == baseline + retry = RunSpec("broken", 7, 5, 0.2, 2, 3) + successful = run_many([retry], bins, bins, problems)["broken"] + assert successful.samples[:1] == result["broken"].samples + for record in baseline.values(): + raw = np.tile(record.base, (len(bins), 1)) + for term in record.best_terms: + for channel, tree in enumerate(term): + raw[:, channel] += record.learning_rate * tree.predict(bins) + np.testing.assert_allclose(raw, record.best_raw) + + +def test_validation_selection_restores_base_when_training_hurts_validation(): + bins = [[0], [1]] + problems = {"a": ([[-1], [1]], [[1], [-1]]), "b": ([[-1], [1]], [[1], [-1]])} + records = run_many( + [RunSpec("b", 0, 5, 0.3, 2, 2), RunSpec("a", 0, 5, 0.1, 1, 2)], bins, bins, problems + ) + for record in records.values(): + assert record.best_round == 0 and record.best_terms == () + np.testing.assert_array_equal(record.best_raw, [[0], [0]]) + assert select_best(records).run_id == "a" # deterministic ID tie, not final train loss + + +def test_rng_key_is_structured_and_independent_of_global_state(): + key = derive_seed(7, "run-α", 2, "tree", "rows") + np.random.seed(987) + assert derive_seed(7, "run-α", 2, "tree", "rows") == key + assert ( + len( + { + key, + derive_seed(7, "run-β", 2, "tree", "rows"), + derive_seed(7, "run-α", 3, "tree", "rows"), + derive_seed(7, "run-α", 2, "tree", "columns"), + } + ) + == 4 + ) + assert derive_seed(1, "ab", 2, "c", "d") != derive_seed(1, "a", 2, "bc", "d") + + +def test_duplicate_run_ids_and_no_successful_selection_rejected(): + bins, specs, problems = fixtures() + with pytest.raises(ValueError): + run_many([specs[0], specs[0]], bins, bins, problems) + failed = run_many([RunSpec("missing", 0, 1, 0.1, 1, 4)], bins, bins, problems) + assert failed["missing"].status == "failed" + with pytest.raises(ValueError): + select_best(failed) + + +def test_fixed_rng_derivation_fixture(): + assert derive_seed(7, "run-α", 2, "tree", "rows") == 6175955064790668999 + + +def test_selection_rejects_incomparable_targets_and_changed_data_identity(): + bins, specs, problems = fixtures() + records = run_many(specs, bins, bins, problems) + with pytest.raises(ValueError): + select_best(records) + original = records["scalar"].problem_id + changed = bins.copy() + changed[-1] = 10 + assert run_many([specs[0]], changed, bins, problems)["scalar"].problem_id != original + + +@pytest.mark.parametrize( + "budget,patience,count,fail", + [(-1, 1, 4, None), (1, 0, 4, None), (1, 1, 0, None), (1, 1, 5, None), (1, 1, 4, 2)], +) +def test_invalid_run_config_is_reported_without_losing_other_runs(budget, patience, count, fail): + bins, specs, problems = fixtures() + problems["bad"] = problems["scalar"] + bad = RunSpec("bad", 0, budget, 0.1, patience, count, fail_round=fail) + records = run_many([bad, specs[0]], bins, bins, problems) + assert records["bad"].status == "failed" + assert records["scalar"].status == "completed" + + +def test_zero_budget_returns_base_and_input_mutation_cannot_change_snapshot(): + bins, specs, problems = fixtures() + result = run_many([RunSpec("scalar", 7, 0, 0.1, 1, 4)], bins, bins, problems)["scalar"] + assert result.rounds == result.best_round == 0 and result.status == "completed" + assert result.best_terms == () + saved = result.best_raw + problems["scalar"][0][:] = 100 + bins[:] = 0 + assert result.best_raw == saved + np.testing.assert_array_equal(result.best_raw, np.zeros((4, 1))) + + +def test_same_seed_distinct_run_ids_have_distinct_sampling_streams(): + x = np.arange(20)[:, None] + y = x.astype(float) + specs = [RunSpec(name, 5, 2, 0.1, 5, 5) for name in ("a", "b")] + records = run_many(specs, x, x, {name: (y, y) for name in ("a", "b")}) + assert records["a"].samples != records["b"].samples + + +def test_identity_missing_payloads_and_metadata_order_are_canonical(): + first = data_identity([0, 1], [[float("nan")], [1.0]], ["x"], {"cuts": [0.5], "version": 1}) + assert first == data_identity( + [0, 1], np.array([[np.nan], [1.0]]), ["x"], {"version": 1, "cuts": [0.5]} + ) + assert first != data_identity([0, 1], [[None], [1.0]], ["x"], {"version": 1, "cuts": [0.5]}) + with pytest.raises(ValueError): + data_identity([0], [[object()]], ["x"], {}) + + +def test_binding_cannot_change_prepared_row_order_even_if_fields_agree(): + prepared = data_identity([10, 20], [[0], [1]], ["x"], {}) + with pytest.raises(ValueError): + bind_identity(prepared, [20, 10], target=([20, 10], [[2], [1]])) + + +@pytest.mark.parametrize("count", [1, 8, 32]) +def test_required_run_counts_keep_individual_results(count): + x = [[0], [1]] + specs = [RunSpec(f"run-{i:02d}", 3, 2, 0.1, 3, 2) for i in range(count)] + problems = {s.run_id: ([[-1], [1]], [[-1], [1]]) for s in specs} + records = run_many(specs, x, x, problems) + assert len(records) == count and all(r.rounds == 2 for r in records.values()) + assert select_best(records).run_id == "run-00" diff --git a/tests/v1/test_scalar_reference.py b/tests/v1/test_scalar_reference.py new file mode 100644 index 0000000..cd562fd --- /dev/null +++ b/tests/v1/test_scalar_reference.py @@ -0,0 +1,85 @@ +"""Hand calculations for the v1 scalar oracle, independent of production.""" + +import numpy as np +import pytest + +from tests.v1.reference.scalar import ( + newton_leaf, + node_score, + row_statistics, + squared_error, + sum_rows, + weighted_mean, +) + + +def test_weighted_base_loss_and_unweighted_derivatives(): + y, weight = np.array([0.0, 2.0, 100.0]), np.array([1.0, 3.0, 0.0]) + assert weighted_mean(y, weight) == 1.5 + loss, gradient, curvature = squared_error(np.zeros(3), y, weight) + assert loss == 1.5 + np.testing.assert_array_equal(gradient, [0.0, -2.0, -100.0]) + np.testing.assert_array_equal(curvature, np.ones(3)) + np.testing.assert_array_equal( + row_statistics(gradient, curvature, weight), [[0.0, 1.0], [-6.0, 3.0], [0.0, 0.0]] + ) + + +def test_newton_leaf_minimizes_independent_quadratic(): + # Q(v)=sum_i w_i * (g_i*v + h_i*v*v/2) + lambda*v*v/2. + g, h, w = [2.0, -1.0, 8.0], [1.0, 2.0, 0.5], [3.0, 2.0, 0.0] + value = newton_leaf(4.0, 7.0, reg_lambda=1.0) + assert value == -0.5 + + def quadratic(v): + return ( + sum(wi * (gi * v + hi * v * v / 2) for gi, hi, wi in zip(g, h, w, strict=True)) + + v * v / 2 + ) + + assert node_score(4.0, 7.0, reg_lambda=1.0) == -quadratic(value) == 1.0 + for alternative in [-2.0, -0.51, -0.49, 0.0, 2.0]: + assert quadratic(alternative) > quadratic(value) + + +def test_row_reduction_preserves_empty_and_zero_weight_rows(): + fields = row_statistics([2.0, -2.0, 100.0], [1.0, 1.0, 1.0], [1.0, 2.0, 0.0]) + np.testing.assert_array_equal(sum_rows(fields, (0, 1, 2)), [-2.0, 3.0]) + np.testing.assert_array_equal(sum_rows(fields, (2,)), [0.0, 0.0]) + np.testing.assert_array_equal(sum_rows(fields, ()), [0.0, 0.0]) + + +@pytest.mark.parametrize("weight", [[0.0, 0.0], [-1.0, 2.0], [np.nan, 1.0], [np.inf, 1.0], [1.0]]) +def test_invalid_training_weights_rejected(weight): + with pytest.raises(ValueError, match="weight"): + weighted_mean([1.0, 2.0], weight) + + +@pytest.mark.parametrize( + "g,h,regularization", + [ + (1.0, 0.0, 0.0), + (0.0, 0.0, 0.0), + (1.0, -1.0, 2.0), + (np.nan, 1.0, 1.0), + (1.0, np.inf, 1.0), + (1.0, 1.0, -1.0), + ], +) +def test_invalid_newton_state_rejected(g, h, regularization): + with pytest.raises(ValueError): + newton_leaf(g, h, reg_lambda=regularization) + + +@pytest.mark.parametrize( + "raw,y", [([np.nan], [1.0]), ([1.0], [np.inf]), ([[1.0]], [1.0]), ([1.0, 2.0], [1.0])] +) +def test_invalid_objective_shape_or_values_rejected(raw, y): + with pytest.raises(ValueError): + squared_error(raw, y) + + +@pytest.mark.parametrize("rows", [(0, 0), (-1,), (2,), (0.5,)]) +def test_invalid_row_selection_rejected(rows): + with pytest.raises(ValueError, match="rows"): + sum_rows(np.ones((2, 2)), rows) diff --git a/tests/v1/test_selection.py b/tests/v1/test_selection.py new file mode 100644 index 0000000..3c7ed3e --- /dev/null +++ b/tests/v1/test_selection.py @@ -0,0 +1,239 @@ +import copy +import hashlib + +import numpy as np +import pytest +from benchmarks.v1.selection import audit, digest, release_test, seal + + +def entry(path): + return {"path": path.name, "sha256": hashlib.sha256(path.read_bytes()).hexdigest()} + + +def example(root): + np.savez(root / "train.npz", row_ids=[0, 1]) + np.savez(root / "valid.npz", row_ids=[2, 3], y=[0.0, 1.0]) + np.savez(root / "test.npz", row_ids=[4, 5], x=[[2.0], [3.0]]) + protocol = dict( + schema="openboost-selection-v1", + application="A1", + fold=0, + identity={ + k: "a" * 64 + for k in ["code", "data", "split", "preprocessing", "environment", "search_design"] + }, + train_rows=entry(root / "train.npz"), + validation=entry(root / "valid.npz"), + test_features=entry(root / "test.npz"), + selection_weights={"rmse": 1.0}, + methods={ + "first": [{"depth": i + 1} for i in range(16)], + "second": [{"leaves": i + 1} for i in range(16)], + }, + ) + records = [] + for method, configs in protocol["methods"].items(): + for i, config in enumerate(configs): + trial_id = f"{method}:{i:02}" + np.savez( + root / f"{trial_id}.npz", + row_ids=[2, 3], + prediction=np.array([0.0, 1.0]) + (16 - i if method == "first" else 0.5 + i), + ) + (root / f"{trial_id}.model").write_bytes(trial_id.encode()) + (root / f"{trial_id}.log").write_text("synthetic fixture\n") + records.append( + dict( + id=trial_id, + config=config, + status="pass", + exit_code=0, + protocol_sha256=digest(protocol), + prediction=entry(root / f"{trial_id}.npz"), + model=entry(root / f"{trial_id}.model"), + log=entry(root / f"{trial_id}.log"), + ) + ) + return protocol, records + + +def test_selects_method_and_trial_from_recomputed_validation(tmp_path): + p, records = example(tmp_path) + # Test features are not read by the selection audit. + features = (tmp_path / "test.npz").read_bytes() + (tmp_path / "test.npz").unlink() + receipt = audit(p, records, tmp_path, digest(p)) + assert receipt["selected"] == "second:00" + assert receipt["scores"]["first:15"]["selection"] == 1.0 + saved = tmp_path / "receipt.json" + receipt_hash = seal(receipt, saved) + with pytest.raises(FileExistsError): + seal(receipt, saved) + (tmp_path / "test.npz").write_bytes(features) + arrays, model = release_test(p, records, saved, tmp_path, digest(p), receipt_hash) + assert arrays["row_ids"].tolist() == [4, 5] + assert model == records[16]["model"] + + +@pytest.mark.parametrize("mutation", ["missing", "failed", "config", "score", "duplicate"]) +def test_incomplete_or_manipulated_search_fails(tmp_path, mutation): + p, records = example(tmp_path) + if mutation == "missing": + records.pop() + elif mutation == "failed": + records[0]["status"] = "timeout" + elif mutation == "config": + records[0]["config"] = {"depth": 99} + elif mutation == "score": + records[0]["validation_score"] = -1000.0 + else: + records[-1] = copy.deepcopy(records[0]) + with pytest.raises(ValueError): + audit(p, records, tmp_path, digest(p)) + + +def test_protocol_cannot_shrink_expected_search(tmp_path): + p, records = example(tmp_path) + pinned = digest(p) + p["methods"].pop("second") + with pytest.raises(ValueError, match="protocol"): + audit(p, records[:16], tmp_path, pinned) + + +def test_changed_model_and_forged_receipt_cannot_release_test(tmp_path): + p, records = example(tmp_path) + receipt = audit(p, records, tmp_path, digest(p)) + saved = tmp_path / "receipt.json" + pinned = seal(receipt, saved) + (tmp_path / records[16]["model"]["path"]).write_bytes(b"replacement") + with pytest.raises(ValueError, match="hash"): + release_test(p, records, saved, tmp_path, digest(p), pinned) + saved.write_text("{}") + with pytest.raises(ValueError, match="receipt"): + release_test(p, records, saved, tmp_path, digest(p), pinned) + + +def test_overlap_fails_even_with_rehashed_features(tmp_path): + p, records = example(tmp_path) + np.savez(tmp_path / "test.npz", row_ids=[1, 4], x=[[2.0], [3.0]]) + p["test_features"] = entry(tmp_path / "test.npz") + for r in records: + r["protocol_sha256"] = digest(p) + receipt = audit(p, records, tmp_path, digest(p)) + saved = tmp_path / "receipt.json" + pinned = seal(receipt, saved) + with pytest.raises(ValueError, match="overlap"): + release_test(p, records, saved, tmp_path, digest(p), pinned) + + +def test_forged_winner_is_rejected_even_if_receipt_hash_is_supplied(tmp_path): + p, records = example(tmp_path) + receipt = audit(p, records, tmp_path, digest(p)) + receipt["selected"] = "first:00" + saved = tmp_path / "receipt.json" + forged_hash = seal(receipt, saved) + with pytest.raises(ValueError, match="independent selection"): + release_test(p, records, saved, tmp_path, digest(p), forged_hash) + + +def test_test_targets_cannot_be_released_as_features(tmp_path): + p, records = example(tmp_path) + np.savez(tmp_path / "test.npz", row_ids=[4, 5], x=[[2.0], [3.0]], y=[1.0, 2.0]) + p["test_features"] = entry(tmp_path / "test.npz") + for record in records: + record["protocol_sha256"] = digest(p) + receipt = audit(p, records, tmp_path, digest(p)) + saved = tmp_path / "receipt.json" + pinned = seal(receipt, saved) + with pytest.raises(ValueError, match="exclude targets"): + release_test(p, records, saved, tmp_path, digest(p), pinned) + + +def test_validation_overlap_fails_before_scoring(tmp_path): + p, records = example(tmp_path) + np.savez(tmp_path / "train.npz", row_ids=[0, 2]) + p["train_rows"] = entry(tmp_path / "train.npz") + with pytest.raises(ValueError, match="overlap"): + audit(p, records, tmp_path, digest(p)) + + +def multi_example(root): + p, records = example(root) + p["application"] = "A6" + p["selection_weights"] = {"rmse_0": 1.0, "rmse_1": 0.01} + np.savez(root / "valid.npz", row_ids=[2, 3], y=[[0.0, 0.0], [0.0, 0.0]]) + p["validation"] = entry(root / "valid.npz") + for i, record in enumerate(records): + path = root / record["prediction"]["path"] + np.savez(path, row_ids=[2, 3], prediction=np.tile([1 + i, 100.0], (2, 1))) + record["prediction"] = entry(path) + record["protocol_sha256"] = digest(p) + return p, records + + +def test_a6_requires_scale_binding(tmp_path): + p, records = multi_example(tmp_path) + with pytest.raises(ValueError): + audit(p, records, tmp_path, digest(p)) + + +def bound_multi_example(root): + import json + + p, records = multi_example(root) + np.savez(root / "targets.npz", row_ids=[0, 1], y=[[-1.0, -100.0], [1.0, 100.0]]) + (root / "scale.json").write_text( + json.dumps(dict(mean=[0.0, 0.0], std=[1.0, 100.0], constant=[False, False])) + ) + p.update(train_targets=entry(root / "targets.npz"), target_scale=entry(root / "scale.json")) + for record in records: + record["protocol_sha256"] = digest(p) + return p, records + + +def test_a6_mean_standardized_rmse_and_no_test_access(tmp_path): + p, records = bound_multi_example(tmp_path) + (tmp_path / "test.npz").unlink() + receipt = audit(p, records, tmp_path, digest(p)) + assert receipt["selected"] == "first:00" + assert receipt["scores"]["first:00"]["selection"] == 1.0 + assert receipt["scores"]["first:01"]["selection"] == 1.5 + + +@pytest.mark.parametrize("mutation", ["weights", "scale", "rows", "targets", "width"]) +def test_a6_forged_binding_rejected(tmp_path, mutation): + import json + + p, records = bound_multi_example(tmp_path) + if mutation == "weights": + p["selection_weights"]["rmse_1"] = 1.0 + elif mutation == "scale": + (tmp_path / "scale.json").write_text( + json.dumps(dict(mean=[0.0, 0.0], std=[1.0, 1.0], constant=[False, False])) + ) + p["target_scale"] = entry(tmp_path / "scale.json") + else: + rows = [1, 0] if mutation == "rows" else [0, 1] + y = [[-1.0, -100.0], [1.0, 100.0]] + if mutation == "targets": + y[1][0] = 50.0 + if mutation == "width": + y = [[-1.0], [1.0]] + np.savez(tmp_path / "targets.npz", row_ids=rows, y=y) + p["train_targets"] = entry(tmp_path / "targets.npz") + for record in records: + record["protocol_sha256"] = digest(p) + with pytest.raises(ValueError): + audit(p, records, tmp_path, digest(p)) + + +def test_a6_scale_changes_winner_as_declared(tmp_path): + p, records = bound_multi_example(tmp_path) + for record, values in zip(records[:2], ([0.0, 150.0], [2.0, 0.0]), strict=True): + path = tmp_path / record["prediction"]["path"] + np.savez(path, row_ids=[2, 3], prediction=np.tile(values, (2, 1))) + record["prediction"] = entry(path) + receipt = audit(p, records, tmp_path, digest(p)) + assert receipt["selected"] == "first:00" + assert receipt["scores"]["first:00"]["selection"] == 0.75 + assert receipt["scores"]["first:01"]["selection"] == 1.0 diff --git a/tests/v1/test_tree_reference.py b/tests/v1/test_tree_reference.py new file mode 100644 index 0000000..4d4e25c --- /dev/null +++ b/tests/v1/test_tree_reference.py @@ -0,0 +1,208 @@ +"""Hand-worked splits, different growth policies, and two-round traces.""" + +import numpy as np +import pytest + +from tests.v1.reference.tree import best_split, boost_squared, enumerate_splits, fit_tree + + +def test_half_gain_and_regularized_leaves(): + x, g = np.array([[0.0], [0.0], [1.0], [1.0]]), [2.0, 2.0, -2.0, -2.0] + split = best_split(enumerate_splits(x, g, np.ones(4))) + assert split.left == (0, 1) and split.right == (2, 3) + assert split.gain == pytest.approx(16 / 3) + tree = fit_tree(x, g, np.ones(4), max_depth=1) + np.testing.assert_allclose(tree.predict(x), [-4 / 3, -4 / 3, 4 / 3, 4 / 3]) + assert best_split(enumerate_splits(x, g, np.ones(4), split_penalty=6.0)) is None + + +def test_two_rounds_recompute_gradient_and_accumulate_coefficients_once(): + x, y = np.array([[0.0], [0.0], [1.0], [1.0]]), [-2.0, -2.0, 2.0, 2.0] + trace = boost_squared(x, y, rounds=2, learning_rate=0.1, max_depth=1) + assert trace.base == 0.0 + first, second = trace.steps + np.testing.assert_array_equal(first.gradient, [2.0, 2.0, -2.0, -2.0]) + np.testing.assert_allclose(first.raw_after, [-2 / 15, -2 / 15, 2 / 15, 2 / 15]) + np.testing.assert_allclose(second.gradient, [28 / 15, 28 / 15, -28 / 15, -28 / 15]) + np.testing.assert_allclose(second.tree.predict(x), [-56 / 45, -56 / 45, 56 / 45, 56 / 45]) + np.testing.assert_allclose(second.raw_after, [-58 / 225, -58 / 225, 58 / 225, 58 / 225]) + np.testing.assert_allclose(trace.predict(x), second.raw_after) + assert second.loss_after < first.loss_after < first.loss_before + + +@pytest.mark.parametrize("missing_gradient,missing_left", [(2.0, True), (-2.0, False)]) +def test_missing_direction_is_chosen_from_routed_rows(missing_gradient, missing_left): + x = np.array([[0.0], [1.0], [np.nan]]) + split = best_split(enumerate_splits(x, [2.0, -2.0, missing_gradient], np.ones(3))) + assert split.condition.missing_left is missing_left + assert 2 in (split.left if missing_left else split.right) + assert set(split.left).isdisjoint(split.right) + assert sorted(split.left + split.right) == [0, 1, 2] + + +def test_exact_ties_choose_first_feature_threshold_and_missing_right(): + x = np.column_stack([np.arange(3), np.arange(3)]) + split = best_split(enumerate_splits(x, [-2.0, 0.0, 2.0], np.ones(3))) + assert (split.condition.feature, split.condition.threshold, split.condition.missing_left) == ( + 0, + 0, + False, + ) + + +def test_missing_only_split_and_all_missing_feature(): + split = best_split(enumerate_splits([[0.0], [np.nan]], [-2.0, 2.0], [1.0, 1.0])) + assert split.left == (0,) and split.right == (1,) + assert best_split(enumerate_splits([[np.nan], [np.nan]], [-2.0, 2.0], [1.0, 1.0])) is None + + +def test_zero_weight_or_zero_curvature_child_is_not_feasible(): + for kwargs in ({"weight": [1.0, 0.0]}, {"curvature": [1.0, 0.0]}): + h = kwargs.pop("curvature", [1.0, 1.0]) + assert best_split(enumerate_splits([[0.0], [1.0]], [-2.0, 2.0], h, **kwargs)) is None + + +def test_empty_node_has_no_candidate(): + assert enumerate_splits([[0.0], [1.0]], [-2.0, 2.0], [1.0, 1.0], rows=()) == () + + +def test_integer_weights_match_duplicated_rows_with_fixed_bins(): + x, y, w = np.array([[0.0], [1.0], [2.0]]), np.array([-2.0, 1.0, 3.0]), np.array([2, 1, 3]) + weighted = boost_squared(x, y, weight=w, rounds=2) + repeated = boost_squared(np.repeat(x, w, axis=0), np.repeat(y, w), rounds=2) + np.testing.assert_allclose(weighted.predict(x), repeated.predict(x), rtol=1e-12, atol=1e-12) + + +def test_cohort_constraint_changes_best_split_and_is_not_training_weight(): + x, g = np.arange(1, 7)[:, None], [-6.0, 1.0, 1.0, 1.0, 1.0, 2.0] + info = np.eye(2)[[0, 1, 0, 1, 0, 1]] + plain = best_split(enumerate_splits(x, g, np.ones(6))) + constrained = best_split( + enumerate_splits(x, g, np.ones(6), information=info, min_information=1.0) + ) + assert plain.condition.threshold == 1 and constrained.condition.threshold == 2 + assert constrained.left == (0, 1) and constrained.right == (2, 3, 4, 5) + # Zero train weight on row 0 must not erase its independent information mass. + candidates = enumerate_splits( + x, + g, + np.ones(6), + weight=[0.0, 1.0, 1.0, 1.0, 1.0, 1.0], + information=info, + min_information=1.0, + ) + assert any(c.condition.threshold == 2 for c in candidates) + separated = np.eye(2)[[0, 0, 0, 1, 1, 1]] + assert ( + best_split(enumerate_splits(x, g, np.ones(6), information=separated, min_information=1.0)) + is None + ) + + +def grid(): + return np.array([[a, b, c] for a in (0, 1) for b in (0, 1) for c in (0, 1)]) + + +def test_best_first_can_grow_deeper_before_a_shallower_leaf(): + x, y = grid(), np.array([-101.0, -99.0, -101.0, -99.0, 80.0, 100.0, 100.0, 120.0]) + depthwise = fit_tree(x, -y, np.ones(8), reg_lambda=0.0, max_depth=3, max_leaves=4) + best_first = fit_tree( + x, -y, np.ones(8), reg_lambda=0.0, max_depth=3, max_leaves=4, policy="best_first" + ) + assert depthwise.nodes[0].condition.feature == best_first.nodes[0].condition.feature == 0 + assert depthwise.nodes[1].condition is not None + assert best_first.nodes[1].condition is None + assert best_first.nodes[3].condition is not None + assert ( + sum(n.condition is None for n in depthwise.nodes) + == sum(n.condition is None for n in best_first.nodes) + == 4 + ) + + +def test_symmetric_aggregates_common_candidates_not_node_winners(): + x, y = grid(), np.array([-12.0, -12.0, -8.0, -8.0, 6.0, 14.0, 6.0, 14.0]) + regular = fit_tree(x, -y, np.ones(8), reg_lambda=0.0, max_depth=2) + symmetric = fit_tree(x, -y, np.ones(8), reg_lambda=0.0, max_depth=2, policy="symmetric") + assert regular.nodes[1].condition.feature == 1 + assert regular.nodes[2].condition.feature == 2 + assert symmetric.nodes[1].condition == symmetric.nodes[2].condition + assert symmetric.nodes[1].condition.feature == 2 + np.testing.assert_allclose( + symmetric.predict(x), [-10.0, -10.0, -10.0, -10.0, 6.0, 14.0, 6.0, 14.0] + ) + + +def test_symmetric_stops_when_full_level_does_not_fit_budget(): + tree = fit_tree( + grid(), + [-12.0, -12.0, -8.0, -8.0, 6.0, 14.0, 6.0, 14.0], + np.ones(8), + reg_lambda=0.0, + policy="symmetric", + max_leaves=3, + ) + assert len(tree.nodes) == 3 + + +@pytest.mark.parametrize("policy", ["depthwise", "best_first", "symmetric"]) +def test_constant_target_and_root_only_budget(policy): + trace = boost_squared([[0.0], [1.0]], [3.0, 3.0], policy=policy) + np.testing.assert_array_equal(trace.predict([[0.0], [1.0]]), [3.0, 3.0]) + assert all(len(step.tree.nodes) == 1 for step in trace.steps) + tree = fit_tree([[0.0], [1.0]], [2.0, -2.0], [1.0, 1.0], max_leaves=1, policy=policy) + assert len(tree.nodes) == 1 + + +@pytest.mark.parametrize( + "kwargs", + [ + {"max_depth": -1}, + {"max_leaves": 0}, + {"policy": "unknown"}, + {"split_penalty": -1.0}, + {"min_child_h": -1.0}, + {"information": [[1.0], [-1.0]]}, + {"min_information": 1.0}, + ], +) +def test_invalid_tree_configuration_rejected(kwargs): + with pytest.raises(ValueError): + fit_tree([[0.0], [1.0]], [2.0, -2.0], [1.0, 1.0], **kwargs) + + +@pytest.mark.parametrize("x", [[[np.inf], [0.0]], [[0.5], [1.0]], [[-1.0], [0.0]], [0.0, 1.0]]) +def test_invalid_fixed_numeric_bins_rejected(x): + with pytest.raises(ValueError, match="bins"): + fit_tree(x, [2.0, -2.0], [1.0, 1.0]) + + +def test_child_leaves_use_actual_routed_rows_and_trace_owns_values(): + x, y = grid(), np.array([-12.0, -12.0, -8.0, -8.0, 6.0, 14.0, 6.0, 14.0]) + trace = boost_squared(x, y, reg_lambda=0.0) + tree = trace.steps[0].tree + for node in tree.nodes: + if node.condition is None: + assert node.value == pytest.approx(sum(y[i] for i in node.rows) / len(node.rows)) + expected = trace.predict(x) + y[:] = 1000.0 + x[:] = 1000 + np.testing.assert_array_equal(trace.predict(grid()), expected) + + +def test_minimum_curvature_and_prediction_schema_are_enforced(): + x = [[0.0], [0.0], [1.0], [1.0]] + assert ( + best_split(enumerate_splits(x, [2.0, 2.0, -2.0, -2.0], np.ones(4), min_child_h=2.1)) is None + ) + tree = fit_tree(x, [2.0, 2.0, -2.0, -2.0], np.ones(4)) + with pytest.raises(ValueError, match="feature"): + tree.predict([[0.0, 1.0]]) + + +@pytest.mark.parametrize( + "kwargs", [{"rounds": -1}, {"learning_rate": np.nan}, {"learning_rate": -0.1}] +) +def test_invalid_boosting_options_rejected(kwargs): + with pytest.raises(ValueError): + boost_squared([[0.0], [1.0]], [-1.0, 1.0], **kwargs) diff --git a/tests/v1/test_worker_data.py b/tests/v1/test_worker_data.py new file mode 100644 index 0000000..70a42c0 --- /dev/null +++ b/tests/v1/test_worker_data.py @@ -0,0 +1,182 @@ +"""Counterexamples for real-data worker packet boundaries.""" + +import copy + +import numpy as np +import pytest +from benchmarks.v1.freeze_preprocessing import prepare +from benchmarks.v1.worker_data import bind + + +def fixture(): + data = dict( + x=np.arange(18, dtype=float).reshape(9, 2), + y=np.column_stack([np.arange(9), np.full(9, 7)]), + group=np.repeat(np.arange(3), 3), + ) + parts = dict(train=np.arange(3), validation=np.arange(3, 6), test=np.arange(6, 9)) + frozen = prepare("parkinsons", data, [parts])[0] + return data, parts, frozen + + +def test_packet_boundary_and_original_target_units(): + data, parts, frozen = fixture() + packets, meta = bind("A6", data, parts, frozen) + assert not any("test" in key for key in packets["worker-input"]) + assert set(packets["test-features"]) == {"row_ids", "x"} + np.testing.assert_array_equal(packets["worker-input"]["y_train"], data["y"][:3]) + assert meta["target_scale"]["mean"] == [1, 7] + assert meta["target_scale"]["constant"] == [False, True] + + +@pytest.mark.parametrize("change", ["order", "encoder", "overlap", "target", "group"]) +def test_corrupted_binding_is_rejected(change): + data, parts, frozen = fixture() + if change == "order": + parts["validation"] = parts["validation"][::-1] + elif change == "encoder": + frozen["encoder"]["median"][0] += 1 + elif change == "overlap": + parts["test"][0] = 0 + elif change == "target": + data["y"][0, 0] += 10 + else: + data["group"][3] = 0 + with pytest.raises(ValueError): + bind("A6", data, parts, frozen) + + +def test_concrete_age_feature_and_support_stay_aligned(): + data, parts, _ = fixture() + data["y"] = data["y"][:, 0] + data["structure"] = np.array([1, 2, 3, 4, 5, 6, 0.5, 8, 9]) + frozen = prepare("concrete", data, [parts])[0] + original = copy.deepcopy(data) + packets, meta = bind("A12", data, parts, frozen) + np.testing.assert_array_equal(packets["test-features"]["x"][:, -1], [0.5, 8, 9]) + assert meta["age_train_range"] == [1, 3] + np.testing.assert_array_equal(packets["test-structure"]["row_ids"], parts["test"]) + np.testing.assert_array_equal(data["x"], original["x"]) + assert packets["worker-input"]["x_train"].shape[1] == 5 + + +def test_categories_and_source_ids_survive_packet_binding(): + data, parts, _ = fixture() + data.pop("group") + data["y"] = np.arange(9) % 2 + data["row_ids"] = np.array([f"adult.data:{i + 1}" for i in range(9)]) + data["categories"] = {"kind": np.array(["a"] * 3 + ["unseen"] * 6)} + frozen = prepare("adult", data, [parts], data["categories"])[0] + packets, _ = bind("A2", data, parts, frozen) + assert frozen["encoder"]["categories"] == {"kind": ["a"]} + np.testing.assert_array_equal(packets["worker-input"]["x_validation"][:, -1], 1) + np.testing.assert_array_equal(packets["validation"]["row_ids"], data["row_ids"][3:6]) + data["row_ids"][1] = data["row_ids"][0] + with pytest.raises(ValueError, match="row identifiers"): + bind("A2", data, parts, frozen) + + +def test_rolling_fold_excludes_future_and_keeps_source_ids(): + data, _, _ = fixture() + data.pop("group") + data["y"] = np.arange(9) + data["dates"] = np.array([f"2020-01-{i + 1:02}" for i in range(9)]) + data["row_ids"] = np.arange(101, 110) + parts = dict(train=np.arange(3), validation=np.arange(3, 5), test=np.arange(5, 7)) + frozen = prepare("bike", data, [parts])[0] + packets, _ = bind("A5", data, parts, frozen) + np.testing.assert_array_equal(packets["test-features"]["row_ids"], [106, 107]) + parts["test"] = np.arange(5, 8) + with pytest.raises(ValueError, match="freeze"): + bind("A5", data, parts, frozen) + + +def test_rolling_gap_and_same_day_boundary_are_rejected(): + data, parts, _ = fixture() + data.pop("group") + data["y"] = np.arange(9) + data["dates"] = np.array([f"2020-01-{i + 1:02}" for i in range(9)]) + frozen = prepare("bike", data, [parts])[0] + data["dates"][3] = data["dates"][2] + with pytest.raises(ValueError, match="dates cross"): + bind("A5", data, parts, frozen) + parts["test"] = np.arange(7, 9) + with pytest.raises(ValueError, match="prefix"): + bind("A5", data, parts, frozen) + + +def insurance_fixture(): + from benchmarks.v1.worker_data import insurance_population + + raw = dict( + x=np.arange(18, dtype=float).reshape(9, 2), + group=np.arange(100, 109), + y=np.array([2, 0, 1, 2, 0, 0, 1, 0, 1]), + exposure=np.arange(1, 10, dtype=float), + paid_total=np.array([30, 0, 0, 70, 0, 0, 50, 0, 60]), + aggregate_eligible=np.array([1, 1, 0, 1, 1, 1, 1, 1, 1], dtype=bool), + severity_policy_row=np.array([0, 0, 3, 3, 6, 8]), + severity_y=np.array([10, 20, 30, 40, 50, 60]), + ) + split = (np.arange(3), np.arange(3, 6), np.arange(6, 9)) + return raw, split, insurance_population + + +def test_paid_claims_share_policy_split_and_keep_individual_amounts(): + raw, split, population = insurance_fixture() + data, folds, _ = population("A8", raw, [split]) + np.testing.assert_array_equal(folds[0][0], [0, 1]) + np.testing.assert_array_equal(data["y"][:2], [10, 20]) + np.testing.assert_array_equal(data["group"][:2], [100, 100]) + parts = dict(zip(("train", "validation", "test"), folds[0], strict=True)) + frozen = prepare("severity", data, [parts])[0] + packets, _ = bind("A8", data, parts, frozen) + assert "weight_train" not in packets["worker-input"] + assert "exposure_train" not in packets["worker-input"] + + +def test_aggregate_annualization_and_exposure_weight_exactly_once(): + raw, split, population = insurance_fixture() + data, folds, _ = population("A9", raw, [split]) + assert 102 not in data["row_ids"] # Positive raw count without paid records excluded. + parts = dict(zip(("train", "validation", "test"), folds[0], strict=True)) + frozen = prepare("aggregate", data, [parts])[0] + packets, _ = bind("A9", data, parts, frozen) + worker = packets["worker-input"] + np.testing.assert_array_equal(worker["y_train"], [30, 0]) + np.testing.assert_array_equal(worker["weight_train"], [1, 2]) + np.testing.assert_array_equal(worker["y_validation"], [17.5, 0, 0]) + np.testing.assert_array_equal(packets["validation"]["weight"], [4, 5, 6]) + assert "exposure_train" not in worker + data["paid_total"][0] += 1 + with pytest.raises(ValueError, match="reconstruct"): + bind("A9", data, parts, frozen) + + +def test_frequency_keeps_integer_counts_and_offset_inputs(): + raw, split, population = insurance_fixture() + data, folds, _ = population("A7", raw, [split]) + parts = dict(zip(("train", "validation", "test"), folds[0], strict=True)) + frozen = prepare("insurance", data, [parts])[0] + packets, _ = bind("A7", data, parts, frozen) + np.testing.assert_array_equal(packets["worker-input"]["y_train"], [2, 0, 1]) + np.testing.assert_array_equal(packets["test-features"]["exposure"], [7, 8, 9]) + assert "weight_train" not in packets["worker-input"] + data["exposure"][0] = 0 + with pytest.raises(ValueError, match="exposure"): + bind("A7", data, parts, frozen) + + +def test_survival_events_and_training_support_are_not_recomputed_on_validation(): + data, parts, _ = fixture() + data.pop("group") + data["y"] = np.arange(1, 10, dtype=float) + data["event"] = np.array([1, 0, 1, 1, 0, 1, 1, 0, 1]) + frozen = prepare("veteran", data, [parts])[0] + packets, meta = bind("A10", data, parts, frozen) + np.testing.assert_array_equal(packets["worker-input"]["event_train"], [1, 0, 1]) + np.testing.assert_array_equal(packets["test-truth"]["event"], [1, 0, 1]) + assert meta["censoring_support"] == frozen["censoring_support"] + frozen["censoring_support"]["survival"][0] = 0 + with pytest.raises(ValueError, match="censoring support"): + bind("A10", data, parts, frozen) diff --git a/uv.lock b/uv.lock index bf16e0a..f8463c0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.13'", @@ -137,18 +137,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" }, ] -[[package]] -name = "aiohttp-cors" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/d89e846a5444b3d5eb8985a6ddb0daef3774928e1bfbce8e84ec97b0ffa7/aiohttp_cors-0.8.1.tar.gz", hash = "sha256:ccacf9cb84b64939ea15f859a146af1f662a6b1d68175754a07315e305fb1403", size = 38626, upload-time = "2025-03-31T14:16:20.048Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/3b/40a68de458904bcc143622015fff2352b6461cd92fd66d3527bf1c6f5716/aiohttp_cors-0.8.1-py3-none-any.whl", hash = "sha256:3180cf304c5c712d626b9162b195b1db7ddf976a2a25172b35bb2448b890a80d", size = 25231, upload-time = "2025-03-31T14:16:18.478Z" }, -] - [[package]] name = "aiosignal" version = "1.4.0" @@ -162,15 +150,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, -] - [[package]] name = "anyio" version = "4.12.0" @@ -389,18 +368,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "colorful" -version = "0.5.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/31/109ef4bedeb32b4202e02ddb133162457adc4eb890a9ed9c05c9dd126ed0/colorful-0.5.8.tar.gz", hash = "sha256:bb16502b198be2f1c42ba3c52c703d5f651d826076817185f0294c1a549a7445", size = 209361, upload-time = "2025-10-29T11:53:21.663Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/11/25cdf9d5fc21efd30134fc74c43702c6f7ef09ebae8ed927f1283403ad8d/colorful-0.5.8-py2.py3-none-any.whl", hash = "sha256:a9381fdda3337fbaba5771991020abc69676afa102646650b759927892875992", size = 201334, upload-time = "2025-10-29T11:53:20.251Z" }, -] - [[package]] name = "coverage" version = "7.13.1" @@ -594,21 +561,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/72/36/c9e24acb19f039f814faea880b3704a3661edaa6739456b73b27540663e3/cupy_cuda12x-13.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:297b4268f839de67ef7865c2202d3f5a0fb8d20bd43360bc51b6e60cb4406447", size = 89750580, upload-time = "2025-08-18T08:25:10.972Z" }, ] -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, -] - [[package]] name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -660,15 +618,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/28/a3/2ad0a0a69662fd4cf556ab8074f0de978ee9b56bff6ddb4e656df4aa9e8e/fastrlock-0.8.3-cp313-cp313-win_amd64.whl", hash = "sha256:8d1d6a28291b4ace2a66bd7b49a9ed9c762467617febdd9ab356b867ed901af8", size = 30472, upload-time = "2024-12-17T11:02:37.983Z" }, ] -[[package]] -name = "filelock" -version = "3.20.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" }, -] - [[package]] name = "frozenlist" version = "1.8.0" @@ -790,15 +739,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] -[[package]] -name = "fsspec" -version = "2025.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/27/954057b0d1f53f086f681755207dda6de6c660ce133c829158e8e8fe7895/fsspec-2025.12.0.tar.gz", hash = "sha256:c505de011584597b1060ff778bb664c1bc022e87921b0e4f10cc9c44f9635973", size = 309748, upload-time = "2025-12-03T15:23:42.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/c7/b64cae5dba3a1b138d7123ec36bb5ccd39d39939f18454407e5468f4763f/fsspec-2025.12.0-py3-none-any.whl", hash = "sha256:8bf1fe301b7d8acfa6e8571e3b1c3d158f909666642431cc78a1b7b4dbc5ec5b", size = 201422, upload-time = "2025-12-03T15:23:41.434Z" }, -] - [[package]] name = "ghp-import" version = "2.1.0" @@ -811,47 +751,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, ] -[[package]] -name = "google-api-core" -version = "2.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "googleapis-common-protos" }, - { name = "proto-plus" }, - { name = "protobuf" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/10/05572d33273292bac49c2d1785925f7bc3ff2fe50e3044cf1062c1dde32e/google_api_core-2.29.0.tar.gz", hash = "sha256:84181be0f8e6b04006df75ddfe728f24489f0af57c96a529ff7cf45bc28797f7", size = 177828, upload-time = "2026-01-08T22:21:39.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/b6/85c4d21067220b9a78cfb81f516f9725ea6befc1544ec9bd2c1acd97c324/google_api_core-2.29.0-py3-none-any.whl", hash = "sha256:d30bc60980daa36e314b5d5a3e5958b0200cb44ca8fa1be2b614e932b75a3ea9", size = 173906, upload-time = "2026-01-08T22:21:36.093Z" }, -] - -[[package]] -name = "google-auth" -version = "2.47.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1-modules" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/3c/ec64b9a275ca22fa1cd3b6e77fefcf837b0732c890aa32d2bd21313d9b33/google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da", size = 323719, upload-time = "2026-01-06T21:55:31.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/18/79e9008530b79527e0d5f79e7eef08d3b179b7f851cfd3a2f27822fbdfa9/google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498", size = 234867, upload-time = "2026-01-06T21:55:28.6Z" }, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.72.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, -] - [[package]] name = "griffe" version = "1.15.0" @@ -864,67 +763,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705, upload-time = "2025-11-10T15:03:13.549Z" }, ] -[[package]] -name = "grpcio" -version = "1.76.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/17/ff4795dc9a34b6aee6ec379f1b66438a3789cd1315aac0cbab60d92f74b3/grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc", size = 5840037, upload-time = "2025-10-21T16:20:25.069Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ff/35f9b96e3fa2f12e1dcd58a4513a2e2294a001d64dec81677361b7040c9a/grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde", size = 11836482, upload-time = "2025-10-21T16:20:30.113Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1c/8374990f9545e99462caacea5413ed783014b3b66ace49e35c533f07507b/grpcio-1.76.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:035d90bc79eaa4bed83f524331d55e35820725c9fbb00ffa1904d5550ed7ede3", size = 6407178, upload-time = "2025-10-21T16:20:32.733Z" }, - { url = "https://files.pythonhosted.org/packages/1e/77/36fd7d7c75a6c12542c90a6d647a27935a1ecaad03e0ffdb7c42db6b04d2/grpcio-1.76.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4215d3a102bd95e2e11b5395c78562967959824156af11fa93d18fdd18050990", size = 7075684, upload-time = "2025-10-21T16:20:35.435Z" }, - { url = "https://files.pythonhosted.org/packages/38/f7/e3cdb252492278e004722306c5a8935eae91e64ea11f0af3437a7de2e2b7/grpcio-1.76.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49ce47231818806067aea3324d4bf13825b658ad662d3b25fada0bdad9b8a6af", size = 6611133, upload-time = "2025-10-21T16:20:37.541Z" }, - { url = "https://files.pythonhosted.org/packages/7e/20/340db7af162ccd20a0893b5f3c4a5d676af7b71105517e62279b5b61d95a/grpcio-1.76.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8cc3309d8e08fd79089e13ed4819d0af72aa935dd8f435a195fd152796752ff2", size = 7195507, upload-time = "2025-10-21T16:20:39.643Z" }, - { url = "https://files.pythonhosted.org/packages/10/f0/b2160addc1487bd8fa4810857a27132fb4ce35c1b330c2f3ac45d697b106/grpcio-1.76.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:971fd5a1d6e62e00d945423a567e42eb1fa678ba89072832185ca836a94daaa6", size = 8160651, upload-time = "2025-10-21T16:20:42.492Z" }, - { url = "https://files.pythonhosted.org/packages/2c/2c/ac6f98aa113c6ef111b3f347854e99ebb7fb9d8f7bb3af1491d438f62af4/grpcio-1.76.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d9adda641db7207e800a7f089068f6f645959f2df27e870ee81d44701dd9db3", size = 7620568, upload-time = "2025-10-21T16:20:45.995Z" }, - { url = "https://files.pythonhosted.org/packages/90/84/7852f7e087285e3ac17a2703bc4129fafee52d77c6c82af97d905566857e/grpcio-1.76.0-cp310-cp310-win32.whl", hash = "sha256:063065249d9e7e0782d03d2bca50787f53bd0fb89a67de9a7b521c4a01f1989b", size = 3998879, upload-time = "2025-10-21T16:20:48.592Z" }, - { url = "https://files.pythonhosted.org/packages/10/30/d3d2adcbb6dd3ff59d6ac3df6ef830e02b437fb5c90990429fd180e52f30/grpcio-1.76.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6ae758eb08088d36812dd5d9af7a9859c05b1e0f714470ea243694b49278e7b", size = 4706892, upload-time = "2025-10-21T16:20:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/a0/00/8163a1beeb6971f66b4bbe6ac9457b97948beba8dd2fc8e1281dce7f79ec/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a", size = 5843567, upload-time = "2025-10-21T16:20:52.829Z" }, - { url = "https://files.pythonhosted.org/packages/10/c1/934202f5cf335e6d852530ce14ddb0fef21be612ba9ecbbcbd4d748ca32d/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c", size = 11848017, upload-time = "2025-10-21T16:20:56.705Z" }, - { url = "https://files.pythonhosted.org/packages/11/0b/8dec16b1863d74af6eb3543928600ec2195af49ca58b16334972f6775663/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465", size = 6412027, upload-time = "2025-10-21T16:20:59.3Z" }, - { url = "https://files.pythonhosted.org/packages/d7/64/7b9e6e7ab910bea9d46f2c090380bab274a0b91fb0a2fe9b0cd399fffa12/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48", size = 7075913, upload-time = "2025-10-21T16:21:01.645Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/093c46e9546073cefa789bd76d44c5cb2abc824ca62af0c18be590ff13ba/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da", size = 6615417, upload-time = "2025-10-21T16:21:03.844Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b6/5709a3a68500a9c03da6fb71740dcdd5ef245e39266461a03f31a57036d8/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397", size = 7199683, upload-time = "2025-10-21T16:21:06.195Z" }, - { url = "https://files.pythonhosted.org/packages/91/d3/4b1f2bf16ed52ce0b508161df3a2d186e4935379a159a834cb4a7d687429/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749", size = 8163109, upload-time = "2025-10-21T16:21:08.498Z" }, - { url = "https://files.pythonhosted.org/packages/5c/61/d9043f95f5f4cf085ac5dd6137b469d41befb04bd80280952ffa2a4c3f12/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00", size = 7626676, upload-time = "2025-10-21T16:21:10.693Z" }, - { url = "https://files.pythonhosted.org/packages/36/95/fd9a5152ca02d8881e4dd419cdd790e11805979f499a2e5b96488b85cf27/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054", size = 3997688, upload-time = "2025-10-21T16:21:12.746Z" }, - { url = "https://files.pythonhosted.org/packages/60/9c/5c359c8d4c9176cfa3c61ecd4efe5affe1f38d9bae81e81ac7186b4c9cc8/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d", size = 4709315, upload-time = "2025-10-21T16:21:15.26Z" }, - { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" }, - { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" }, - { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" }, - { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" }, - { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" }, - { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, - { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, - { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, - { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, - { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, - { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, - { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, - { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, - { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, - { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, - { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, - { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, -] - [[package]] name = "grpclib" version = "0.4.9" @@ -978,18 +816,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - [[package]] name = "iniconfig" version = "2.3.0" @@ -999,238 +825,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] -[[package]] -name = "jax" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "opt-einsum", marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/1e/267f59c8fb7f143c3f778c76cb7ef1389db3fd7e4540f04b9f42ca90764d/jax-0.6.2.tar.gz", hash = "sha256:a437d29038cbc8300334119692744704ca7941490867b9665406b7f90665cd96", size = 2334091, upload-time = "2025-06-17T23:10:27.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/a8/97ef0cbb7a17143ace2643d600a7b80d6705b2266fc31078229e406bdef2/jax-0.6.2-py3-none-any.whl", hash = "sha256:bb24a82dc60ccf704dcaf6dbd07d04957f68a6c686db19630dd75260d1fb788c", size = 2722396, upload-time = "2025-06-17T23:10:25.293Z" }, -] - -[package.optional-dependencies] -cuda12 = [ - { name = "jax-cuda12-plugin", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, extra = ["with-cuda"], marker = "python_full_version < '3.11'" }, - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] - -[[package]] -name = "jax" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "jaxlib", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "opt-einsum", marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e6/25/5efb46e5492076622d9150ed394da97ef9aad393aa52f7dd7e980f836e1f/jax-0.8.2.tar.gz", hash = "sha256:1a685ded06a8223a7b52e45e668e406049dbbead02873f2b5a4d881ba7b421ae", size = 2505776, upload-time = "2025-12-18T18:41:59.274Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/f7/ae4ecf183d9693cd5fcce7ee063c5e54f173b66dc80a8a79951861e1b557/jax-0.8.2-py3-none-any.whl", hash = "sha256:d0478c5dc74406441efcd25731166a65ee782f13c352fa72dc7d734351909355", size = 2925344, upload-time = "2025-12-18T18:39:38.645Z" }, -] - -[package.optional-dependencies] -cuda12 = [ - { name = "jax-cuda12-plugin", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, extra = ["with-cuda"], marker = "python_full_version >= '3.11'" }, - { name = "jaxlib", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] - -[[package]] -name = "jax-cuda12-pjrt" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/8b/096a12d91b1bc76a13cd8e63f2d8eae5ca9b5693b5ee2687e46be3b5d779/jax_cuda12_pjrt-0.6.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:22faf020d2e8f7ca1e2915633241f7df7678b73c7078f5f0b2f113248337f7de", size = 111228681, upload-time = "2025-06-17T23:11:55.179Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8e/21c21b4335fce1c022c339da5e6b6249c246ad062e924d28fb0eda4bcef0/jax_cuda12_pjrt-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8cd9ead7948ea2c778a508fef5d1159e8b7abf4fccc7037c3fe1dbfcd95012dc", size = 125263999, upload-time = "2025-06-17T23:11:59.986Z" }, -] - -[[package]] -name = "jax-cuda12-pjrt" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/57/633225274a1a8812607eebba6be88d63fa5bec3d87ac6979721f02d32f52/jax_cuda12_pjrt-0.8.2-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:717a1b196a642409ce195ddf031c20bbeadcc886f55e49a1d3f4927373aeedae", size = 147769954, upload-time = "2025-12-18T18:39:41.686Z" }, - { url = "https://files.pythonhosted.org/packages/53/f2/44ad0ce1d115f0f6be10f4af0ca05a18afb838b06e6ca6b01ba4b0137421/jax_cuda12_pjrt-0.8.2-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:e3bab41ca7c48e4163db9e7efd271b3aa85f0fe45f5ed0708d6bbed93a59f977", size = 153684891, upload-time = "2025-12-18T18:39:47.172Z" }, -] - -[[package]] -name = "jax-cuda12-plugin" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "jax-cuda12-pjrt", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/29/4b8822ca459da39bda9be7454908ae4e29d88cfb99b480b641cbb063af7a/jax_cuda12_plugin-0.6.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:bc5c3a75d05519b4d326e4669d0f7ad0fe0f0acf875f9313d913748ccca5a9ea", size = 15873729, upload-time = "2025-06-17T23:12:05.046Z" }, - { url = "https://files.pythonhosted.org/packages/4d/3d/f543bab6ef7eebb9840d618fb2272bdcc0e990e60aa012f14a3564532823/jax_cuda12_plugin-0.6.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:1751f88989269b3cdb0dfe4f7b072a6442149818c9bc98c3a395c8acaf910a79", size = 15879965, upload-time = "2025-06-17T23:12:07.323Z" }, - { url = "https://files.pythonhosted.org/packages/9e/99/90f81c660bf662698be17e8b959d8302682c5cd5ce0729c0bfc883d8affe/jax_cuda12_plugin-0.6.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:2cd8e279a59a38ba0c978a831e13adeb6ee9e4572fba387c7975ba3ad535dd38", size = 15873970, upload-time = "2025-06-17T23:12:09.492Z" }, - { url = "https://files.pythonhosted.org/packages/ad/00/e733c87a2fb7265c96f48c991a896552c873de949217823d519288724d91/jax_cuda12_plugin-0.6.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:0896cbb308d95291e205cd89d254029dee3a1df43d66e9831331a9afd2d27870", size = 15879563, upload-time = "2025-06-17T23:12:11.439Z" }, - { url = "https://files.pythonhosted.org/packages/1d/53/6ea0db7230ac3dcb26b732452f4f2e2c6868b75d3603be19cee388fef279/jax_cuda12_plugin-0.6.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:6c9b002d13b1fcb9403713eedd3876a227ad1ffbdfb3811b1f9f89af4c25a5f7", size = 15867462, upload-time = "2025-06-17T23:12:14.093Z" }, - { url = "https://files.pythonhosted.org/packages/a5/db/e6643143caf573273eedb991cb1af2bea964b84594a8887802eb0b6ba64a/jax_cuda12_plugin-0.6.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:febd099f970d350eb8fa5a2c9a2fb4b0ea7b3d6a89df1496663edfa7afe590e5", size = 15876401, upload-time = "2025-06-17T23:12:15.964Z" }, - { url = "https://files.pythonhosted.org/packages/b4/10/74fbae1c1bb9d11b113c62e03c445bdf0aaaa4981bc13d2de8e3f4f503ca/jax_cuda12_plugin-0.6.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:773efa8b55a837406c561f0ef02144dda9019181193760ec5419eec9dd2b9aac", size = 15868561, upload-time = "2025-06-17T23:12:18.189Z" }, - { url = "https://files.pythonhosted.org/packages/2b/96/53928ad62ecddbf76f4c413025fdeab5a90adf7fbd970d800162399e504a/jax_cuda12_plugin-0.6.2-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:db4c6103c912d8cd1adf94c34d313bb4760ca7f01c897ca7cd62e65f27994199", size = 15876276, upload-time = "2025-06-17T23:12:20.361Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/f1c1790d77ccad3587e6c97045b567006a8050fec151581d4cf883779e25/jax_cuda12_plugin-0.6.2-cp313-cp313t-manylinux2014_aarch64.whl", hash = "sha256:ed5316ca1818db7ef53230ee0a41398d3a60942e361dfb857a952eb4d92fc8d7", size = 15964355, upload-time = "2025-06-17T23:12:22.176Z" }, - { url = "https://files.pythonhosted.org/packages/07/af/c3224aafbc1d2d7654359a5410319bf15361067635bd3616cf5ad3e16af2/jax_cuda12_plugin-0.6.2-cp313-cp313t-manylinux2014_x86_64.whl", hash = "sha256:83345f52f610cdb8e90044566d8e120864150b8090968c8ab6dd8e0bfb9a6a9f", size = 16037754, upload-time = "2025-06-17T23:12:24.066Z" }, -] - -[package.optional-dependencies] -with-cuda = [ - { name = "nvidia-cublas-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cuda-nvcc-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cudnn-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cufft-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cusolver-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-cusparse-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-nccl-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-nvjitlink-cu12", marker = "python_full_version < '3.11'" }, - { name = "nvidia-nvshmem-cu12", marker = "python_full_version < '3.11'" }, -] - -[[package]] -name = "jax-cuda12-plugin" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "jax-cuda12-pjrt", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/22/224c9718920cdc3eed72587f1c734e516e268c59ae1d6807e9e6a57dbcd0/jax_cuda12_plugin-0.8.2-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:70d33222484ad5c375b8f8357b7c23cacb844f6ecfc39567f8dd47fde6e87858", size = 5676989, upload-time = "2025-12-18T18:39:50.334Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1f/135f820e960fcaaa1673556745e92b153ebb80a81a274493e0c4aec439ff/jax_cuda12_plugin-0.8.2-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:0b0a3304ce7e494acd8d9c593490c112a32cdb6010fe1afc584d9e41fd863167", size = 5686662, upload-time = "2025-12-18T18:39:51.708Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e1/b57bdb540fdaddbd4d226491773f00f30fedd0d241f0511a9e26db7a83e5/jax_cuda12_plugin-0.8.2-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:403d5e07731b5cdac3bd9fb3f448bd8480062cb2c0ab61ea2ad23fcd0a65479a", size = 5670354, upload-time = "2025-12-18T18:39:53.01Z" }, - { url = "https://files.pythonhosted.org/packages/60/a5/3a4f6585d530516f8a70494e9e916a611e01733ce3174dacb45d48a5ee63/jax_cuda12_plugin-0.8.2-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:20165861b3d3e66ebb2c0f63a547d1d5ee17ea44ac3be7153c7908c9ca8c88f3", size = 5684435, upload-time = "2025-12-18T18:39:54.213Z" }, - { url = "https://files.pythonhosted.org/packages/8c/84/42fe91a595d119b51a53acfa43266c400efd09804b9e1945c8658d5ff524/jax_cuda12_plugin-0.8.2-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:637387dc3408cd204562668502f9e95f76c6edde0a6d2e48f055162dc2aebf0d", size = 5670615, upload-time = "2025-12-18T18:39:55.428Z" }, - { url = "https://files.pythonhosted.org/packages/1c/38/4ba2486f95fcf2120723932feacdded438e785258148b18a703cd1177e41/jax_cuda12_plugin-0.8.2-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:82c6798be66bf8c773386918e4c8e5cd8119753f3bfb3ca4bbc46818283750c6", size = 5684138, upload-time = "2025-12-18T18:39:56.574Z" }, - { url = "https://files.pythonhosted.org/packages/74/94/0b6651b6a37a35b9ae30011e4698dc1705165e3fef2d4d54a4cbad852bd3/jax_cuda12_plugin-0.8.2-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:1b4828242d57f233b394d17ebaa599c503c1fb9b7c754012a06eb84dbc935fc8", size = 5685718, upload-time = "2025-12-18T18:39:57.765Z" }, - { url = "https://files.pythonhosted.org/packages/dd/0a/a5c40bd3d870e02edfbe45ab1c9d69e3ec19f6b2bf3c714cf8d8717b2fe6/jax_cuda12_plugin-0.8.2-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:dd4f7c34d4512ff5a36fd1b01584ef7781cad615e3f9e71880eae2f4998e5108", size = 5691685, upload-time = "2025-12-18T18:39:59.035Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/2ed072a156a26f47b0f3a537dcd70ad027b5afafd5c903221bc64b0bb2e3/jax_cuda12_plugin-0.8.2-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:58c51473fc622e03138035985f741833564d70a4bd5a2178f61b62cdaa32ff94", size = 5671398, upload-time = "2025-12-18T18:40:00.328Z" }, - { url = "https://files.pythonhosted.org/packages/27/58/a5a27d4677d6890570f7e58cecd51891469cb620e6f64c8faed4935d93d0/jax_cuda12_plugin-0.8.2-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:a5898bac1d8ab6020b54546440256409f2c66bcbbb3a1099ca473c84843addad", size = 5684741, upload-time = "2025-12-18T18:40:01.507Z" }, - { url = "https://files.pythonhosted.org/packages/3d/79/55891d955dc680f915916680fc88b351a9ca02664fc90b7eae7659951d89/jax_cuda12_plugin-0.8.2-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:d68a6d8b4a45ee561746bac7a6468da8203832626b0b39ad4ac43011f61f875d", size = 5685940, upload-time = "2025-12-18T18:40:03.174Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/42d5c9adf99f47c4f909029ff4f99cb65caef13044984fcfcb6ce89f536c/jax_cuda12_plugin-0.8.2-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:377e4be17e22dde0343b3f3c05bf69235b3dbf11d766cca9c5a93da47971dcb7", size = 5692440, upload-time = "2025-12-18T18:40:04.411Z" }, -] - -[package.optional-dependencies] -with-cuda = [ - { name = "nvidia-cublas-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvcc-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, -] - -[[package]] -name = "jaxlib" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "ml-dtypes", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/c5/41598634c99cbebba46e6777286fb76abc449d33d50aeae5d36128ca8803/jaxlib-0.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4601b2b5dc8c23d6afb293eacfb9aec4e1d1871cb2f29c5a151d103e73b0f8", size = 54298019, upload-time = "2025-06-17T23:10:36.916Z" }, - { url = "https://files.pythonhosted.org/packages/81/af/db07d746cd5867d5967528e7811da53374e94f64e80a890d6a5a4b95b130/jaxlib-0.6.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4205d098ce8efb5f7fe2fe5098bae6036094dc8d8829f5e0e0d7a9b155326336", size = 79440052, upload-time = "2025-06-17T23:10:41.282Z" }, - { url = "https://files.pythonhosted.org/packages/7e/d8/b7ae9e819c62c1854dbc2c70540a5c041173fbc8bec5e78ab7fd615a4aee/jaxlib-0.6.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:c087a0eb6fb7f6f8f54d56f4730328dfde5040dd3b5ddfa810e7c28ea7102b42", size = 89917034, upload-time = "2025-06-17T23:10:45.897Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e5/87e91bc70569ac5c3e3449eefcaf47986e892f10cfe1d5e5720dceae3068/jaxlib-0.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:153eaa51f778b60851720729d4f461a91edd9ba3932f6f3bc598d4413870038b", size = 57896337, upload-time = "2025-06-17T23:10:50.179Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ee/6899b0aed36a4acc51319465ddd83c7c300a062a9e236cceee00984ffe0b/jaxlib-0.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a208ff61c58128d306bb4e5ad0858bd2b0960f2c1c10ad42c548f74a60c0020e", size = 54300346, upload-time = "2025-06-17T23:10:54.591Z" }, - { url = "https://files.pythonhosted.org/packages/e6/03/34bb6b346609079a71942cfbf507892e3c877a06a430a0df8429c455cebc/jaxlib-0.6.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:11eae7e05bc5a79875da36324afb9eddd4baeaef2a0386caf6d4f3720b9aef28", size = 79438425, upload-time = "2025-06-17T23:10:58.356Z" }, - { url = "https://files.pythonhosted.org/packages/80/02/49b05cbab519ffd3cb79586336451fbbf8b6523f67128a794acc9f179000/jaxlib-0.6.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:335d7e3515ce78b52a410136f46aa4a7ea14d0e7d640f34e1e137409554ad0ac", size = 89920354, upload-time = "2025-06-17T23:11:03.086Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7a/93b28d9452b46c15fc28dd65405672fc8a158b35d46beabaa0fe9631afb0/jaxlib-0.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6815509997d6b05e5c9daa7994b9ad473ce3e8c8a17bdbbcacc3c744f76f7a0", size = 57895707, upload-time = "2025-06-17T23:11:07.074Z" }, - { url = "https://files.pythonhosted.org/packages/ac/db/05e702d2534e87abf606b1067b46a273b120e6adc7d459696e3ce7399317/jaxlib-0.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d8a684a8be949dd87dd4acc97101b4106a0dc9ad151ec891da072319a57b99", size = 54301644, upload-time = "2025-06-17T23:11:10.977Z" }, - { url = "https://files.pythonhosted.org/packages/0d/8a/b0a96887b97a25d45ae2c30e4acecd2f95acd074c18ec737dda8c5cc7016/jaxlib-0.6.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:87ec2dc9c3ed9ab936eec8535160c5fbd2c849948559f1c5daa75f63fabe5942", size = 79439161, upload-time = "2025-06-17T23:11:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e8/71c2555431edb5dd115cf86a7b599aa7e1be26728d89ae59aa11251d299c/jaxlib-0.6.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f1dd09b481a93c1d4c750013f467f74194493ba7bd29fcd4d1cec16e3a214f65", size = 89942952, upload-time = "2025-06-17T23:11:19.181Z" }, - { url = "https://files.pythonhosted.org/packages/de/3a/06849113c844b86d20174df54735c84202ccf82cbd36d805f478c834418b/jaxlib-0.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:921dbd4db214eba19a29ba9f2450d880e08b2b2c7b968f28cc89da3e62366af4", size = 57919603, upload-time = "2025-06-17T23:11:23.207Z" }, - { url = "https://files.pythonhosted.org/packages/af/38/bed4279c2a3407820ed8bcd72dbad43c330ada35f88fafe9952b35abf785/jaxlib-0.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bff67b188133ce1f0111c7b163ac321fd646b59ed221ea489063e2e0f85cb967", size = 54300638, upload-time = "2025-06-17T23:11:26.372Z" }, - { url = "https://files.pythonhosted.org/packages/52/dc/9e35a1dc089ddf3d6be53ef2e6ba4718c5b6c0f90bccc535a20edac0c895/jaxlib-0.6.2-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:70498837caf538bd458ff6858c8bfd404db82015aba8f663670197fa9900ff02", size = 79439983, upload-time = "2025-06-17T23:11:30.016Z" }, - { url = "https://files.pythonhosted.org/packages/34/16/e93f0184b80a4e1ad38c6998aa3a2f7569c0b0152cbae39f7572393eda04/jaxlib-0.6.2-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:f94163f14c8fd3ba93ae14b631abacf14cb031bba0b59138869984b4d10375f8", size = 89941720, upload-time = "2025-06-17T23:11:34.62Z" }, - { url = "https://files.pythonhosted.org/packages/06/b9/ea50792ee0333dba764e06c305fe098bce1cb938dcb66fbe2fc47ef5dd02/jaxlib-0.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:b977604cd36c74b174d25ed685017379468138eb747d865f75e466cb273c801d", size = 57919073, upload-time = "2025-06-17T23:11:39.344Z" }, - { url = "https://files.pythonhosted.org/packages/09/ce/9596391c104a0547fcaf6a8c72078bbae79dbc8e7f0843dc8318f6606328/jaxlib-0.6.2-cp313-cp313t-manylinux2014_aarch64.whl", hash = "sha256:39cf9555f85ae1ce2e2c1a59fc71f2eca4f9867a7cb934fef881ba56b11371d1", size = 79579638, upload-time = "2025-06-17T23:11:43.054Z" }, - { url = "https://files.pythonhosted.org/packages/10/79/f6e80f7f4cacfc9f03e64ac57ecb856b140de7c2f939b25f8dcf1aff63f9/jaxlib-0.6.2-cp313-cp313t-manylinux2014_x86_64.whl", hash = "sha256:3abd536e44b05fb1657507e3ff1fc3691f99613bae3921ecab9e82f27255f784", size = 90066675, upload-time = "2025-06-17T23:11:47.454Z" }, -] - -[[package]] -name = "jaxlib" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "ml-dtypes", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/87/0a44b1a5c558e6d8e4fd796d4f9efe5c8cac2b3013ab7349968c65931fa4/jaxlib-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:490bf0cb029c73c65c9431124b86cdc95082dbc1fb76fc549d24d75da33e5454", size = 55929353, upload-time = "2025-12-18T18:40:35.844Z" }, - { url = "https://files.pythonhosted.org/packages/d1/d2/b37c86ee35d9ea7ee67c81e9166b31e18aa3784e1b96e8a60f52bbb8c9c0/jaxlib-0.8.2-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:bb89be452b1b808d3f88fc01c415b364a260be4cc7ac120c038009f6150a32dc", size = 74548611, upload-time = "2025-12-18T18:40:39.67Z" }, - { url = "https://files.pythonhosted.org/packages/65/7d/9bb1cd620d8093098203b17d227a902939afec00da1c63cb719a9fe89525/jaxlib-0.8.2-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:ccf77da917a20935247c990691decfcbdd06c25ef0ac94d914a04aadb22f714c", size = 80127195, upload-time = "2025-12-18T18:40:43.795Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f1/56d830c7fcf1736cbfb11d8cf79c1932f826f319d2467becb02933df3ba9/jaxlib-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:dffc22b5b732b9556d92c918b251c61bcc046617c4dbb51e1f7a656587fddffb", size = 60338464, upload-time = "2025-12-18T18:40:47.427Z" }, - { url = "https://files.pythonhosted.org/packages/c1/77/18ac0ac08c76bf12ed47b0c2d7d35f3fc3d065bd105b36937901eab1455c/jaxlib-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:023de6f3f56da2af7037970996500586331fdb50b530ecbb54b9666da633bd00", size = 55938204, upload-time = "2025-12-18T18:40:50.859Z" }, - { url = "https://files.pythonhosted.org/packages/33/c5/fa809591cbddc0d7bbef9c95962a0b521ae4a168b0ff375cadf37840b97d/jaxlib-0.8.2-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:3b16e50c5b730c9dd0a49e55f1acfaa722b00b1af0522a591558dcc0464252f2", size = 74550881, upload-time = "2025-12-18T18:40:54.491Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/e386c4bbfda3fb326a01594cc46c8ac90cdeeeacee4c553d9e3848f75893/jaxlib-0.8.2-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:2b9789bd08f8b0cc5a5c12ae896fe432d5942e32e417091b8b5a96a9a6fd5cf1", size = 80135127, upload-time = "2025-12-18T18:40:58.808Z" }, - { url = "https://files.pythonhosted.org/packages/bf/4c/0c90b1e2b47fdf34cd352a01c42c2628d115a6f015d4a3230060bb0d97af/jaxlib-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:f472cc72e3058e50b5f0230b236d5a1183bf6c3d5423d2a52eff07bcf34908de", size = 60361039, upload-time = "2025-12-18T18:41:02.367Z" }, - { url = "https://files.pythonhosted.org/packages/c5/22/c0ec75e43a13b2457d78d509f49b49a57fa302ffced4f4a2778e428cb0a6/jaxlib-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d006db96be020c8165212a1216372f8acac4ff4f8fb067743d694ef2b301ace", size = 55939058, upload-time = "2025-12-18T18:41:06.199Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e2/2d3eff7a49ca37ef6929bf67b8ab4c933ab53a115060e60c239702028568/jaxlib-0.8.2-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:7c304f3a016965b9d1f5239a8a0399a73925f5604fe914c5ca66ecf734bf6422", size = 74550207, upload-time = "2025-12-18T18:41:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e0/91e5762a7ddb6351b07c742ca407cd28e26043d6945d6228b6c1b0881a45/jaxlib-0.8.2-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:1bfbcf6c3de221784fa4cdb6765a09d71cb4298b15626b3d0409b3dfcd8a8667", size = 80133534, upload-time = "2025-12-18T18:41:14.193Z" }, - { url = "https://files.pythonhosted.org/packages/85/68/25b38673b07a808616ce7b6efb3eed491f983f3373a09cbbd03f67178563/jaxlib-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:f205e91c3a152a2a76c0bc59a6a2de03e87ec261b91e8812922777185e7b08f5", size = 60358239, upload-time = "2025-12-18T18:41:17.661Z" }, - { url = "https://files.pythonhosted.org/packages/bc/da/753c4b16297576e33cb41bf605d27fefd016867d365861c43c505afd1579/jaxlib-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f28edac8c226fc07fa3e8af6f9defede8ac2c307429e3291edce8739d39becc9", size = 56035453, upload-time = "2025-12-18T18:41:21.004Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/891f967b01a60de1dbcb8c40b6fee28cc39c670c27c919756c41d8c89ebe/jaxlib-0.8.2-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:7da8127557c786264049ae55460d1b8d04cc3cdf0403a087f2fc1e6d313ec722", size = 74661142, upload-time = "2025-12-18T18:41:24.454Z" }, - { url = "https://files.pythonhosted.org/packages/e2/5c/3f1476cd6cbc0e2aa661cb750489739aeda500473d91dc79837b5bc9247f/jaxlib-0.8.2-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:28eec1a4e0639a0d8702cea3cb70dd3663053dbfa344452994ea48dc6ceadaa5", size = 80238500, upload-time = "2025-12-18T18:41:28.647Z" }, - { url = "https://files.pythonhosted.org/packages/d8/9d/dca93d916bf8664d7a2bb73ea3d219028dabbe382c31774348963287356a/jaxlib-0.8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:beffb004e7eeb5c9afb24439e2b2cf45a4ee3e3e8adf45e355edf2af62acf8b8", size = 55943240, upload-time = "2025-12-18T18:41:32.095Z" }, - { url = "https://files.pythonhosted.org/packages/f0/47/7407d010db7f5ec1c25a8b8d379defc0c8b4daaaa829c88355e03c0ad314/jaxlib-0.8.2-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:68108dff0de74adc468016be9a19f80efe48c660c0d5a122287094b44b092afc", size = 74560018, upload-time = "2025-12-18T18:41:36.154Z" }, - { url = "https://files.pythonhosted.org/packages/5e/27/2e6032727e41ce74914277478021140947af59127d68aa9e6f3776b428fd/jaxlib-0.8.2-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:e6a97dfb0232eed9a2bb6e3828e4f682dbac1a7fea840bfda574cae2dbf5faf9", size = 80156235, upload-time = "2025-12-18T18:41:40.227Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8c/af5a00b07a446414edf6b84a7397eab02cf01ba44b6ae1fce7798ce4c127/jaxlib-0.8.2-cp314-cp314-win_amd64.whl", hash = "sha256:05b958f497e49824c432e734bb059723b7dfe69e2ad696a9f9c8ad82fff7c3f8", size = 62673493, upload-time = "2025-12-18T18:41:43.991Z" }, - { url = "https://files.pythonhosted.org/packages/4d/eb/ad70fe97fda465d536625bef39ee381a7f8fed1f1bf0bc296510bac32ec5/jaxlib-0.8.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964626f581beab31ee6826b228fcc2ec5181b05cecf94a528dff97921c145dbc", size = 56037334, upload-time = "2025-12-18T18:41:47.407Z" }, - { url = "https://files.pythonhosted.org/packages/34/97/0741440c66a49ec3702f6c28a5608c7543243b1728c3f465505ed5bfe7d2/jaxlib-0.8.2-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:a397ea7dcb37d689ce79173eeb99b2f1347637a36be9a27f20ae6848bfc58bfc", size = 74661591, upload-time = "2025-12-18T18:41:51.285Z" }, - { url = "https://files.pythonhosted.org/packages/7c/c4/388797324c201830ac414562eb6697fa38837f40852bdc4d0f464d65889c/jaxlib-0.8.2-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:aa8701b6356f098e8452c3cec762fb5f706fcb8f67ffd65964f63982479aa23b", size = 80236629, upload-time = "2025-12-18T18:41:56.05Z" }, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -1252,33 +846,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - [[package]] name = "librt" version = "0.7.8" @@ -1660,52 +1227,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055, upload-time = "2025-12-03T14:26:10.184Z" }, ] -[[package]] -name = "ml-dtypes" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/3a/c5b855752a70267ff729c349e650263adb3c206c29d28cc8ea7ace30a1d5/ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c", size = 679735, upload-time = "2025-11-17T22:31:31.367Z" }, - { url = "https://files.pythonhosted.org/packages/41/79/7433f30ee04bd4faa303844048f55e1eb939131c8e5195a00a96a0939b64/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a", size = 5051883, upload-time = "2025-11-17T22:31:33.658Z" }, - { url = "https://files.pythonhosted.org/packages/10/b1/8938e8830b0ee2e167fc75a094dea766a1152bde46752cd9bfc57ee78a82/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270", size = 5030369, upload-time = "2025-11-17T22:31:35.595Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a3/51886727bd16e2f47587997b802dd56398692ce8c6c03c2e5bb32ecafe26/ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2", size = 210738, upload-time = "2025-11-17T22:31:37.43Z" }, - { url = "https://files.pythonhosted.org/packages/c6/5e/712092cfe7e5eb667b8ad9ca7c54442f21ed7ca8979745f1000e24cf8737/ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90", size = 679734, upload-time = "2025-11-17T22:31:39.223Z" }, - { url = "https://files.pythonhosted.org/packages/4f/cf/912146dfd4b5c0eea956836c01dcd2fce6c9c844b2691f5152aca196ce4f/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040", size = 5056165, upload-time = "2025-11-17T22:31:41.071Z" }, - { url = "https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483", size = 5034975, upload-time = "2025-11-17T22:31:42.758Z" }, - { url = "https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb", size = 210742, upload-time = "2025-11-17T22:31:44.068Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c9/64230ef14e40aa3f1cb254ef623bf812735e6bec7772848d19131111ac0d/ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de", size = 160709, upload-time = "2025-11-17T22:31:46.557Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927, upload-time = "2025-11-17T22:31:48.182Z" }, - { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464, upload-time = "2025-11-17T22:31:50.135Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002, upload-time = "2025-11-17T22:31:52.001Z" }, - { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222, upload-time = "2025-11-17T22:31:53.742Z" }, - { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793, upload-time = "2025-11-17T22:31:55.358Z" }, - { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" }, - { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" }, - { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" }, - { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" }, - { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" }, - { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" }, - { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" }, - { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" }, - { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" }, - { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" }, - { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" }, - { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" }, - { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" }, - { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" }, - { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" }, - { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" }, - { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" }, - { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" }, - { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" }, -] - [[package]] name = "modal" version = "1.3.0.post1" @@ -1731,76 +1252,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/87/3c4c9136c2d553307d8547e39bb2ed755e165b8a8272b6990af9912fef5c/modal-1.3.0.post1-py3-none-any.whl", hash = "sha256:98d338aade676bafd7c80645ef7b4302a6a7219338124ef8b210854a043ec7ed", size = 744342, upload-time = "2025-12-20T02:47:53.726Z" }, ] -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, -] - -[[package]] -name = "msgpack" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/a2/3b68a9e769db68668b25c6108444a35f9bd163bb848c0650d516761a59c0/msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2", size = 81318, upload-time = "2025-10-08T09:14:38.722Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e1/2b720cc341325c00be44e1ed59e7cfeae2678329fbf5aa68f5bda57fe728/msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87", size = 83786, upload-time = "2025-10-08T09:14:40.082Z" }, - { url = "https://files.pythonhosted.org/packages/71/e5/c2241de64bfceac456b140737812a2ab310b10538a7b34a1d393b748e095/msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251", size = 398240, upload-time = "2025-10-08T09:14:41.151Z" }, - { url = "https://files.pythonhosted.org/packages/b7/09/2a06956383c0fdebaef5aa9246e2356776f12ea6f2a44bd1368abf0e46c4/msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a", size = 406070, upload-time = "2025-10-08T09:14:42.821Z" }, - { url = "https://files.pythonhosted.org/packages/0e/74/2957703f0e1ef20637d6aead4fbb314330c26f39aa046b348c7edcf6ca6b/msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f", size = 393403, upload-time = "2025-10-08T09:14:44.38Z" }, - { url = "https://files.pythonhosted.org/packages/a5/09/3bfc12aa90f77b37322fc33e7a8a7c29ba7c8edeadfa27664451801b9860/msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f", size = 398947, upload-time = "2025-10-08T09:14:45.56Z" }, - { url = "https://files.pythonhosted.org/packages/4b/4f/05fcebd3b4977cb3d840f7ef6b77c51f8582086de5e642f3fefee35c86fc/msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9", size = 64769, upload-time = "2025-10-08T09:14:47.334Z" }, - { url = "https://files.pythonhosted.org/packages/d0/3e/b4547e3a34210956382eed1c85935fff7e0f9b98be3106b3745d7dec9c5e/msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa", size = 71293, upload-time = "2025-10-08T09:14:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271, upload-time = "2025-10-08T09:14:49.967Z" }, - { url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914, upload-time = "2025-10-08T09:14:50.958Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962, upload-time = "2025-10-08T09:14:51.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183, upload-time = "2025-10-08T09:14:53.477Z" }, - { url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454, upload-time = "2025-10-08T09:14:54.648Z" }, - { url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341, upload-time = "2025-10-08T09:14:56.328Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747, upload-time = "2025-10-08T09:14:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z" }, - { url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755, upload-time = "2025-10-08T09:15:00.48Z" }, - { url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" }, - { url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" }, - { url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" }, - { url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" }, - { url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" }, - { url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" }, - { url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" }, - { url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" }, - { url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" }, - { url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" }, - { url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" }, - { url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" }, - { url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" }, - { url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" }, - { url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" }, - { url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127, upload-time = "2025-10-08T09:15:24.408Z" }, - { url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981, upload-time = "2025-10-08T09:15:25.812Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885, upload-time = "2025-10-08T09:15:27.22Z" }, - { url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658, upload-time = "2025-10-08T09:15:28.4Z" }, - { url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290, upload-time = "2025-10-08T09:15:29.764Z" }, - { url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234, upload-time = "2025-10-08T09:15:31.022Z" }, - { url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391, upload-time = "2025-10-08T09:15:32.265Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787, upload-time = "2025-10-08T09:15:33.219Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453, upload-time = "2025-10-08T09:15:34.225Z" }, - { url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264, upload-time = "2025-10-08T09:15:35.61Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076, upload-time = "2025-10-08T09:15:36.619Z" }, - { url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242, upload-time = "2025-10-08T09:15:37.647Z" }, - { url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509, upload-time = "2025-10-08T09:15:38.794Z" }, - { url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957, upload-time = "2025-10-08T09:15:40.238Z" }, - { url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910, upload-time = "2025-10-08T09:15:41.505Z" }, - { url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197, upload-time = "2025-10-08T09:15:42.954Z" }, - { url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772, upload-time = "2025-10-08T09:15:43.954Z" }, - { url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868, upload-time = "2025-10-08T09:15:44.959Z" }, -] - [[package]] name = "multidict" version = "6.7.0" @@ -1994,32 +1445,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, -] - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - [[package]] name = "numba" version = "0.63.1" @@ -2233,199 +1658,24 @@ wheels = [ ] [[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0", size = 590537124, upload-time = "2025-03-07T01:43:53.556Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, - { url = "https://files.pythonhosted.org/packages/70/61/7d7b3c70186fb651d0fbd35b01dbfc8e755f69fd58f817f3d0f642df20c3/nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af", size = 567544208, upload-time = "2025-03-07T01:53:30.535Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" +name = "nvidia-nccl-cu12" +version = "2.27.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed", size = 9533318, upload-time = "2025-03-07T01:40:10.421Z" }, - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, - { url = "https://files.pythonhosted.org/packages/41/bc/83f5426095d93694ae39fe1311431b5d5a9bb82e48bf0dd8e19be2765942/nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e", size = 7015759, upload-time = "2025-03-07T01:51:11.355Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625, upload-time = "2025-06-26T04:11:04.496Z" }, + { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, ] [[package]] -name = "nvidia-cuda-nvcc-cu12" -version = "12.9.86" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/48/b54a06168a2190572a312bfe4ce443687773eb61367ced31e064953dd2f7/nvidia_cuda_nvcc_cu12-12.9.86-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:5d6a0d32fdc7ea39917c20065614ae93add6f577d840233237ff08e9a38f58f0", size = 40546229, upload-time = "2025-06-05T20:01:53.357Z" }, - { url = "https://files.pythonhosted.org/packages/d6/5c/8cc072436787104bbbcbde1f76ab4a0d89e68f7cebc758dd2ad7913a43d0/nvidia_cuda_nvcc_cu12-12.9.86-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44e1eca4d08926193a558d2434b1bf83d57b4d5743e0c431c0c83d51da1df62b", size = 39411138, upload-time = "2025-06-05T20:01:43.182Z" }, - { url = "https://files.pythonhosted.org/packages/d2/9e/c71c53655a65d7531c89421c282359e2f626838762f1ce6180ea0bbebd29/nvidia_cuda_nvcc_cu12-12.9.86-py3-none-win_amd64.whl", hash = "sha256:8ed7f0b17dea662755395be029376db3b94fed5cbb17c2d35cc866c5b1b84099", size = 34669845, upload-time = "2025-06-05T20:11:56.308Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8", size = 43106076, upload-time = "2025-03-07T01:41:59.817Z" }, - { url = "https://files.pythonhosted.org/packages/45/51/52a3d84baa2136cc8df15500ad731d74d3a1114d4c123e043cb608d4a32b/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909", size = 73586838, upload-time = "2025-03-07T01:52:13.483Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d", size = 965265, upload-time = "2025-03-07T01:39:43.533Z" }, - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, - { url = "https://files.pythonhosted.org/packages/30/a5/a515b7600ad361ea14bfa13fb4d6687abf500adc270f19e89849c0590492/nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8", size = 944318, upload-time = "2025-03-07T01:51:01.794Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8", size = 705026878, upload-time = "2025-06-06T21:52:51.348Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, - { url = "https://files.pythonhosted.org/packages/3d/90/0bd6e586701b3a890fd38aa71c387dab4883d619d6e5ad912ccbd05bfd67/nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e", size = 692992268, upload-time = "2025-06-06T21:55:18.114Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a", size = 193109211, upload-time = "2025-03-07T01:44:56.873Z" }, - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ec/ce1629f1e478bb5ccd208986b5f9e0316a78538dd6ab1d0484f012f8e2a1/nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7", size = 192216559, upload-time = "2025-03-07T01:53:57.106Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0", size = 267229841, upload-time = "2025-03-07T01:46:54.356Z" }, - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, - { url = "https://files.pythonhosted.org/packages/13/c0/76ca8551b8a84146ffa189fec81c26d04adba4bc0dbe09cd6e6fd9b7de04/nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34", size = 256720438, upload-time = "2025-03-07T01:54:39.898Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc", size = 288117129, upload-time = "2025-03-07T01:47:40.407Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, - { url = "https://files.pythonhosted.org/packages/62/07/f3b2ad63f8e3d257a599f422ae34eb565e70c41031aecefa3d18b62cabd1/nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd", size = 284937404, upload-time = "2025-03-07T01:55:07.742Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/1c/857979db0ef194ca5e21478a0612bcdbbe59458d7694361882279947b349/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a", size = 322400625, upload-time = "2025-06-26T04:11:04.496Z" }, - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, - { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7", size = 38386204, upload-time = "2025-03-07T01:49:43.612Z" }, - { url = "https://files.pythonhosted.org/packages/ed/d7/34f02dad2e30c31b10a51f6b04e025e5dd60e5f936af9045a9b858a05383/nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f", size = 268553710, upload-time = "2025-03-07T01:56:24.13Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.3.20" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/9d/3dd98852568fb845ec1f7902c90a22b240fe1cbabda411ccedf2fd737b7b/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b0b960da3842212758e4fa4696b94f129090b30e5122fea3c5345916545cff0", size = 124484616, upload-time = "2025-08-04T20:24:59.172Z" }, - { url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, -] - -[[package]] -name = "openboost" -version = "1.0.0rc1" -source = { editable = "." } -dependencies = [ - { name = "joblib" }, - { name = "numba" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +name = "openboost" +version = "1.0.0.dev0" +source = { editable = "." } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] [package.optional-dependencies] -all = [ - { name = "cupy-cuda12x" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and sys_platform == 'linux'" }, - { name = "jax", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, - { name = "modal" }, - { name = "numba-cuda" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "ray", extra = ["default"] }, - { name = "ruff" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "torch" }, - { name = "xgboost" }, -] bench = [ { name = "modal" }, { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -2437,8 +1687,6 @@ cuda = [ { name = "numba-cuda" }, ] dev = [ - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and sys_platform == 'linux'" }, - { name = "jax", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, { name = "modal" }, { name = "pytest" }, { name = "pytest-cov" }, @@ -2448,27 +1696,11 @@ dev = [ { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "xgboost" }, ] -distributed = [ - { name = "ray", extra = ["default"] }, -] -jax = [ - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, extra = ["cuda12"], marker = "python_full_version < '3.11'" }, - { name = "jax", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, extra = ["cuda12"], marker = "python_full_version >= '3.11'" }, -] -sklearn = [ - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] test = [ - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and sys_platform == 'linux'" }, - { name = "jax", version = "0.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and sys_platform == 'linux'" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-xdist" }, ] -torch = [ - { name = "torch" }, -] [package.dev-dependencies] dev = [ @@ -2488,27 +1720,18 @@ dev = [ [package.metadata] requires-dist = [ { name = "cupy-cuda12x", marker = "extra == 'cuda'", specifier = ">=13.0" }, - { name = "jax", marker = "sys_platform == 'linux' and extra == 'test'", specifier = ">=0.4" }, - { name = "jax", extras = ["cuda12"], marker = "extra == 'jax'", specifier = ">=0.4" }, - { name = "joblib", specifier = ">=1.3" }, { name = "modal", marker = "extra == 'bench'", specifier = ">=0.73" }, - { name = "numba", specifier = ">=0.60" }, { name = "numba-cuda", marker = "extra == 'cuda'", specifier = ">=0.23" }, { name = "numpy", specifier = ">=1.24" }, - { name = "openboost", extras = ["cuda", "torch", "distributed", "sklearn", "dev"], marker = "extra == 'all'" }, - { name = "openboost", extras = ["test", "bench", "sklearn"], marker = "extra == 'dev'" }, + { name = "openboost", extras = ["test", "bench"], marker = "extra == 'dev'" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=7.0" }, { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=4.0" }, { name = "pytest-xdist", marker = "extra == 'test'", specifier = ">=3.0" }, - { name = "ray", extras = ["default"], marker = "extra == 'distributed'", specifier = ">=2.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4" }, { name = "scikit-learn", marker = "extra == 'bench'", specifier = ">=1.0" }, - { name = "scikit-learn", marker = "extra == 'sklearn'", specifier = ">=1.0" }, - { name = "scipy", specifier = ">=1.10" }, - { name = "torch", marker = "extra == 'torch'", specifier = ">=2.0" }, { name = "xgboost", marker = "extra == 'bench'", specifier = ">=2.0" }, ] -provides-extras = ["cuda", "torch", "jax", "distributed", "sklearn", "test", "bench", "dev", "all"] +provides-extras = ["cuda", "test", "bench", "dev"] [package.metadata.requires-dev] dev = [ @@ -2525,104 +1748,6 @@ dev = [ { name = "xgboost", specifier = ">=2.0" }, ] -[[package]] -name = "opencensus" -version = "0.11.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core" }, - { name = "opencensus-context" }, - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/15/a7/a46dcffa1b63084f9f17fe3c8cb20724c4c8f91009fd0b2cfdb27d5d2b35/opencensus-0.11.4.tar.gz", hash = "sha256:cbef87d8b8773064ab60e5c2a1ced58bbaa38a6d052c41aec224958ce544eff2", size = 64966, upload-time = "2024-01-03T18:04:07.085Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/ed/9fbdeb23a09e430d87b7d72d430484b88184633dc50f6bfb792354b6f661/opencensus-0.11.4-py2.py3-none-any.whl", hash = "sha256:a18487ce68bc19900336e0ff4655c5a116daf10c1b3685ece8d971bddad6a864", size = 128225, upload-time = "2024-01-03T18:04:05.127Z" }, -] - -[[package]] -name = "opencensus-context" -version = "0.1.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/96/3b6f638f6275a8abbd45e582448723bffa29c1fb426721dedb5c72f7d056/opencensus-context-0.1.3.tar.gz", hash = "sha256:a03108c3c10d8c80bb5ddf5c8a1f033161fa61972a9917f9b9b3a18517f0088c", size = 4066, upload-time = "2022-08-03T22:20:22.359Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/68/162c97ea78c957d68ecf78a5c5041d2e25bd5562bdf5d89a6cbf7f8429bf/opencensus_context-0.1.3-py2.py3-none-any.whl", hash = "sha256:073bb0590007af276853009fac7e4bab1d523c3f03baf4cb4511ca38967c6039", size = 5060, upload-time = "2022-08-03T22:20:20.352Z" }, -] - -[[package]] -name = "opentelemetry-api" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, -] - -[[package]] -name = "opentelemetry-proto" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, -] - -[[package]] -name = "opt-einsum" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004, upload-time = "2024-09-26T14:33:24.483Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932, upload-time = "2024-09-26T14:33:23.039Z" }, -] - [[package]] name = "packaging" version = "25.0" @@ -2668,15 +1793,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] -[[package]] -name = "prometheus-client" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, -] - [[package]] name = "propcache" version = "0.4.1" @@ -2791,18 +1907,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] -[[package]] -name = "proto-plus" -version = "1.27.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/89/9cbe2f4bba860e149108b683bc2efec21f14d5f7ed6e25562ad86acbc373/proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4", size = 56158, upload-time = "2025-12-16T13:46:25.729Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/24/3b7a0818484df9c28172857af32c2397b6d8fcd99d9468bd4684f98ebf0a/proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82", size = 50205, upload-time = "2025-12-16T13:46:24.76Z" }, -] - [[package]] name = "protobuf" version = "6.33.2" @@ -2818,175 +1922,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, ] -[[package]] -name = "py-spy" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/e2/ff811a367028b87e86714945bb9ecb5c1cc69114a8039a67b3a862cef921/py_spy-0.4.1.tar.gz", hash = "sha256:e53aa53daa2e47c2eef97dd2455b47bb3a7e7f962796a86cc3e7dbde8e6f4db4", size = 244726, upload-time = "2025-07-31T19:33:25.172Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/e3/3a32500d845bdd94f6a2b4ed6244982f42ec2bc64602ea8fcfe900678ae7/py_spy-0.4.1-py2.py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:809094208c6256c8f4ccadd31e9a513fe2429253f48e20066879239ba12cd8cc", size = 3682508, upload-time = "2025-07-31T19:33:13.753Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bf/e4d280e9e0bec71d39fc646654097027d4bbe8e04af18fb68e49afcff404/py_spy-0.4.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:1fb8bf71ab8df95a95cc387deed6552934c50feef2cf6456bc06692a5508fd0c", size = 1796395, upload-time = "2025-07-31T19:33:15.325Z" }, - { url = "https://files.pythonhosted.org/packages/df/79/9ed50bb0a9de63ed023aa2db8b6265b04a7760d98c61eb54def6a5fddb68/py_spy-0.4.1-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee776b9d512a011d1ad3907ed53ae32ce2f3d9ff3e1782236554e22103b5c084", size = 2034938, upload-time = "2025-07-31T19:33:17.194Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/36862e3eea59f729dfb70ee6f9e14b051d8ddce1aa7e70e0b81d9fe18536/py_spy-0.4.1-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:532d3525538254d1859b49de1fbe9744df6b8865657c9f0e444bf36ce3f19226", size = 2658968, upload-time = "2025-07-31T19:33:18.916Z" }, - { url = "https://files.pythonhosted.org/packages/08/f8/9ea0b586b065a623f591e5e7961282ec944b5fbbdca33186c7c0296645b3/py_spy-0.4.1-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4972c21890b6814017e39ac233c22572c4a61fd874524ebc5ccab0f2237aee0a", size = 2147541, upload-time = "2025-07-31T19:33:20.565Z" }, - { url = "https://files.pythonhosted.org/packages/68/fb/bc7f639aed026bca6e7beb1e33f6951e16b7d315594e7635a4f7d21d63f4/py_spy-0.4.1-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6a80ec05eb8a6883863a367c6a4d4f2d57de68466f7956b6367d4edd5c61bb29", size = 2763338, upload-time = "2025-07-31T19:33:22.202Z" }, - { url = "https://files.pythonhosted.org/packages/e1/da/fcc9a9fcd4ca946ff402cff20348e838b051d69f50f5d1f5dca4cd3c5eb8/py_spy-0.4.1-py2.py3-none-win_amd64.whl", hash = "sha256:d92e522bd40e9bf7d87c204033ce5bb5c828fca45fa28d970f58d71128069fdc", size = 1818784, upload-time = "2025-07-31T19:33:23.802Z" }, -] - -[[package]] -name = "pyasn1" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, -] - -[[package]] -name = "pyasn1-modules" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, - { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, - { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, - { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, - { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, - { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, - { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, - { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, - { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, - { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, - { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, - { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, - { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, - { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, - { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, - { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, - { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, - { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, - { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, - { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, - { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, - { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, - { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, - { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, - { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, - { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, - { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, - { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, - { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, - { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -3142,70 +2077,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, ] -[[package]] -name = "ray" -version = "2.53.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "filelock" }, - { name = "jsonschema" }, - { name = "msgpack" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "pyyaml" }, - { name = "requests" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/99/21986c7f8135dafbf7c49229c52faaa9d2d365db7d86fffe978dde8ee967/ray-2.53.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4db914a0a6dd608fa49c066929a1282745a2dbd73caee67d7b80fe684ca65bdd", size = 69473649, upload-time = "2025-12-20T16:05:40.58Z" }, - { url = "https://files.pythonhosted.org/packages/70/d9/58b5426a3f11993851db3c93841358cebdddd948153481d355b720f31f9d/ray-2.53.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4108280d8a1cb90d7d68e5c954c35e63b8bb9a4ba15f88c5e7da0e2025647712", size = 71342662, upload-time = "2025-12-20T16:05:46.936Z" }, - { url = "https://files.pythonhosted.org/packages/c5/05/4aa32370b313481c2d1d41cb53ec786daebdb2ef665b01ef2ac43d9cf457/ray-2.53.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:4dbb5fce1364763f29741055f50abe33cf726397141f9cc0e845dd3cc963e455", size = 72188620, upload-time = "2025-12-20T16:05:52.817Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c6/21efe5886898421df20078a333b0984eade7d7aa4bdc68a336f0c66db27e/ray-2.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:90faf630d20b6abf3135997fb3edb5842134aff92e04ee709865db04816d97ef", size = 27200553, upload-time = "2025-12-20T16:05:57.655Z" }, - { url = "https://files.pythonhosted.org/packages/bf/64/d5c29a4b014d8b9a624203a88b67630072c1d6960425dbf7a1f0fa5d6b74/ray-2.53.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bd3ec4c342776ddac23ae2b108c64f5939f417ccc4875900d586c7c978463269", size = 69479296, upload-time = "2025-12-20T16:06:05.111Z" }, - { url = "https://files.pythonhosted.org/packages/c6/41/9e19d1e5d9458a5ba157c36642e2874bcb22fddbd7c1e77b668e5afc3f3d/ray-2.53.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:a0bbb98b0b0f25a3ee075ca10171e1260e70b6bc690cd509ecd7ce1228af854d", size = 71463449, upload-time = "2025-12-20T16:06:10.983Z" }, - { url = "https://files.pythonhosted.org/packages/63/de/58c19906b0dd16ea06b4f2465b7327f5f180e6b6e1c8c9b610d7c589ea5f/ray-2.53.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:eb000c17f7301071fdd15c44c4cd3ac0f7953bb4c7c227e61719fe7048195bcd", size = 72305102, upload-time = "2025-12-20T16:06:17.989Z" }, - { url = "https://files.pythonhosted.org/packages/b1/43/72cc1cfe17d26abe62a793eab10445f9546dce24192b85a6cd0cdc47ed86/ray-2.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:4a1bb3fe09ab4cd0d16ddc96b9f60c9ed83b3f93b87aa8506e0d3b746fd4e825", size = 27194174, upload-time = "2025-12-20T16:06:23.042Z" }, - { url = "https://files.pythonhosted.org/packages/b2/44/562718a634e63e8ef7985285288a167d4af62bc2a7decce3300cf937776a/ray-2.53.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:d8b95d047d947493803fb8417aea31225dcacdab15afdc75b8a238901949d457", size = 69463763, upload-time = "2025-12-20T16:06:28.685Z" }, - { url = "https://files.pythonhosted.org/packages/38/68/8e59b8413f3751fe7ce8b98ee8787d13964b47a4043587950790a9dd2151/ray-2.53.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:65e2ce58d3dc6baa3cf45824d889c1968ebde565ee54dfd80a98af8f31af8e4a", size = 71504450, upload-time = "2025-12-20T16:06:34.922Z" }, - { url = "https://files.pythonhosted.org/packages/2a/db/978a50d264565ca42e2a4bf115ec9a1f04f19ca5e620e6aa2f280747b644/ray-2.53.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:14f46363e9b4cf0c1c8b4d8623ec337c5bd408377831b5e5b50067930137bbca", size = 72370424, upload-time = "2025-12-20T16:06:40.821Z" }, - { url = "https://files.pythonhosted.org/packages/8d/6c/bba6f22a9d83ee8f236000ba315f0c197bdc79888b4fa42fd762f729cbbd/ray-2.53.0-cp312-cp312-win_amd64.whl", hash = "sha256:b828c147f9ff2f277b1d254e4fe9a746fdfaee7e313a93a97c7edf4dae9b81a4", size = 27178106, upload-time = "2025-12-20T16:06:45.594Z" }, - { url = "https://files.pythonhosted.org/packages/3d/38/450cf9cf3c490fa4cc6d470597f819444da60f85579d2b34b95ee79fcb6f/ray-2.53.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:85b472ab6fb8f1189f8cef81913fd91b24dd69b3fa7dcca7e144827bd924f6c0", size = 69409819, upload-time = "2025-12-20T16:06:50.668Z" }, - { url = "https://files.pythonhosted.org/packages/71/5e/d452970b07174d5e4f8688abae889d01321b51ced827db1f1d1cb7d56d44/ray-2.53.0-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:7196e5358dfcc8211be864f45e6dfe4827202df294af3c7a76ff8fbc080e0522", size = 71409529, upload-time = "2025-12-20T16:06:56.2Z" }, - { url = "https://files.pythonhosted.org/packages/cb/84/50b317a125617a638a64694c12f56183edd5df01828a35fa4c55c7b13c66/ray-2.53.0-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:73dbbaa7962a7f5e38aa8cf9483e0e9817205e989aa3dc859c738c2af1ae01df", size = 72283961, upload-time = "2025-12-20T16:07:05.831Z" }, -] - -[package.optional-dependencies] -default = [ - { name = "aiohttp" }, - { name = "aiohttp-cors" }, - { name = "colorful" }, - { name = "grpcio" }, - { name = "opencensus" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, - { name = "py-spy" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "smart-open" }, - { name = "virtualenv" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - [[package]] name = "requests" version = "2.32.5" @@ -3234,140 +2105,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, - { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, - { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, - { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, - { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, - { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, - { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, - { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, - { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, - { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, - { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, - { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, - { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, - { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, - { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, - { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, - { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, - { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, - { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, - { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, - { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, - { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, - { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, - { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, - { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, - { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, - { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, - { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, - { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, -] - -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - [[package]] name = "ruff" version = "0.14.10" @@ -3402,10 +2139,10 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, + { name = "joblib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -3451,10 +2188,10 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, + { name = "joblib" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } wheels = [ @@ -3504,7 +2241,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } wheels = [ @@ -3565,7 +2302,7 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" } wheels = [ @@ -3631,15 +2368,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" }, ] -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - [[package]] name = "shellingham" version = "1.5.4" @@ -3658,30 +2386,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "smart-open" -version = "7.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/9a/0a7acb748b86e2922982366d780ca4b16c33f7246fa5860d26005c97e4f3/smart_open-7.5.0.tar.gz", hash = "sha256:f394b143851d8091011832ac8113ea4aba6b92e6c35f6e677ddaaccb169d7cb9", size = 53920, upload-time = "2025-11-08T21:38:40.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/95/bc978be7ea0babf2fb48a414b6afaad414c6a9e8b1eafc5b8a53c030381a/smart_open-7.5.0-py3-none-any.whl", hash = "sha256:87e695c5148bbb988f15cec00971602765874163be85acb1c9fb8abc012e6599", size = 63940, upload-time = "2025-11-08T21:38:39.024Z" }, -] - -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, -] - [[package]] name = "synchronicity" version = "0.11.1" @@ -3761,81 +2465,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] -[[package]] -name = "torch" -version = "2.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/56/9577683b23072075ed2e40d725c52c2019d71a972fab8e083763da8e707e/torch-2.9.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1cc208435f6c379f9b8fdfd5ceb5be1e3b72a6bdf1cb46c0d2812aa73472db9e", size = 104207681, upload-time = "2025-11-12T15:19:56.48Z" }, - { url = "https://files.pythonhosted.org/packages/38/45/be5a74f221df8f4b609b78ff79dc789b0cc9017624544ac4dd1c03973150/torch-2.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9fd35c68b3679378c11f5eb73220fdcb4e6f4592295277fbb657d31fd053237c", size = 899794036, upload-time = "2025-11-12T15:21:01.886Z" }, - { url = "https://files.pythonhosted.org/packages/67/95/a581e8a382596b69385a44bab2733f1273d45c842f5d4a504c0edc3133b6/torch-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af70e3be4a13becba4655d6cc07dcfec7ae844db6ac38d6c1dafeb245d17d65", size = 110969861, upload-time = "2025-11-12T15:21:30.145Z" }, - { url = "https://files.pythonhosted.org/packages/ad/51/1756dc128d2bf6ea4e0a915cb89ea5e730315ff33d60c1ff56fd626ba3eb/torch-2.9.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a83b0e84cc375e3318a808d032510dde99d696a85fe9473fc8575612b63ae951", size = 74452222, upload-time = "2025-11-12T15:20:46.223Z" }, - { url = "https://files.pythonhosted.org/packages/15/db/c064112ac0089af3d2f7a2b5bfbabf4aa407a78b74f87889e524b91c5402/torch-2.9.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:62b3fd888277946918cba4478cf849303da5359f0fb4e3bfb86b0533ba2eaf8d", size = 104220430, upload-time = "2025-11-12T15:20:31.705Z" }, - { url = "https://files.pythonhosted.org/packages/56/be/76eaa36c9cd032d3b01b001e2c5a05943df75f26211f68fae79e62f87734/torch-2.9.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d033ff0ac3f5400df862a51bdde9bad83561f3739ea0046e68f5401ebfa67c1b", size = 899821446, upload-time = "2025-11-12T15:20:15.544Z" }, - { url = "https://files.pythonhosted.org/packages/47/cc/7a2949e38dfe3244c4df21f0e1c27bce8aedd6c604a587dd44fc21017cb4/torch-2.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:0d06b30a9207b7c3516a9e0102114024755a07045f0c1d2f2a56b1819ac06bcb", size = 110973074, upload-time = "2025-11-12T15:21:39.958Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ce/7d251155a783fb2c1bb6837b2b7023c622a2070a0a72726ca1df47e7ea34/torch-2.9.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:52347912d868653e1528b47cafaf79b285b98be3f4f35d5955389b1b95224475", size = 74463887, upload-time = "2025-11-12T15:20:36.611Z" }, - { url = "https://files.pythonhosted.org/packages/0f/27/07c645c7673e73e53ded71705045d6cb5bae94c4b021b03aa8d03eee90ab/torch-2.9.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:da5f6f4d7f4940a173e5572791af238cb0b9e21b1aab592bd8b26da4c99f1cd6", size = 104126592, upload-time = "2025-11-12T15:20:41.62Z" }, - { url = "https://files.pythonhosted.org/packages/19/17/e377a460603132b00760511299fceba4102bd95db1a0ee788da21298ccff/torch-2.9.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:27331cd902fb4322252657f3902adf1c4f6acad9dcad81d8df3ae14c7c4f07c4", size = 899742281, upload-time = "2025-11-12T15:22:17.602Z" }, - { url = "https://files.pythonhosted.org/packages/b1/1a/64f5769025db846a82567fa5b7d21dba4558a7234ee631712ee4771c436c/torch-2.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:81a285002d7b8cfd3fdf1b98aa8df138d41f1a8334fd9ea37511517cedf43083", size = 110940568, upload-time = "2025-11-12T15:21:18.689Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ab/07739fd776618e5882661d04c43f5b5586323e2f6a2d7d84aac20d8f20bd/torch-2.9.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:c0d25d1d8e531b8343bea0ed811d5d528958f1dcbd37e7245bc686273177ad7e", size = 74479191, upload-time = "2025-11-12T15:21:25.816Z" }, - { url = "https://files.pythonhosted.org/packages/20/60/8fc5e828d050bddfab469b3fe78e5ab9a7e53dda9c3bdc6a43d17ce99e63/torch-2.9.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c29455d2b910b98738131990394da3e50eea8291dfeb4b12de71ecf1fdeb21cb", size = 104135743, upload-time = "2025-11-12T15:21:34.936Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b7/6d3f80e6918213babddb2a37b46dbb14c15b14c5f473e347869a51f40e1f/torch-2.9.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:524de44cd13931208ba2c4bde9ec7741fd4ae6bfd06409a604fc32f6520c2bc9", size = 899749493, upload-time = "2025-11-12T15:24:36.356Z" }, - { url = "https://files.pythonhosted.org/packages/a6/47/c7843d69d6de8938c1cbb1eba426b1d48ddf375f101473d3e31a5fc52b74/torch-2.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:545844cc16b3f91e08ce3b40e9c2d77012dd33a48d505aed34b7740ed627a1b2", size = 110944162, upload-time = "2025-11-12T15:21:53.151Z" }, - { url = "https://files.pythonhosted.org/packages/28/0e/2a37247957e72c12151b33a01e4df651d9d155dd74d8cfcbfad15a79b44a/torch-2.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5be4bf7496f1e3ffb1dd44b672adb1ac3f081f204c5ca81eba6442f5f634df8e", size = 74830751, upload-time = "2025-11-12T15:21:43.792Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f7/7a18745edcd7b9ca2381aa03353647bca8aace91683c4975f19ac233809d/torch-2.9.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:30a3e170a84894f3652434b56d59a64a2c11366b0ed5776fab33c2439396bf9a", size = 104142929, upload-time = "2025-11-12T15:21:48.319Z" }, - { url = "https://files.pythonhosted.org/packages/f4/dd/f1c0d879f2863ef209e18823a988dc7a1bf40470750e3ebe927efdb9407f/torch-2.9.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8301a7b431e51764629208d0edaa4f9e4c33e6df0f2f90b90e261d623df6a4e2", size = 899748978, upload-time = "2025-11-12T15:23:04.568Z" }, - { url = "https://files.pythonhosted.org/packages/1f/9f/6986b83a53b4d043e36f3f898b798ab51f7f20fdf1a9b01a2720f445043d/torch-2.9.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2e1c42c0ae92bf803a4b2409fdfed85e30f9027a66887f5e7dcdbc014c7531db", size = 111176995, upload-time = "2025-11-12T15:22:01.618Z" }, - { url = "https://files.pythonhosted.org/packages/40/60/71c698b466dd01e65d0e9514b5405faae200c52a76901baf6906856f17e4/torch-2.9.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:2c14b3da5df416cf9cb5efab83aa3056f5b8cd8620b8fde81b4987ecab730587", size = 74480347, upload-time = "2025-11-12T15:21:57.648Z" }, - { url = "https://files.pythonhosted.org/packages/48/50/c4b5112546d0d13cc9eaa1c732b823d676a9f49ae8b6f97772f795874a03/torch-2.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1edee27a7c9897f4e0b7c14cfc2f3008c571921134522d5b9b5ec4ebbc69041a", size = 74433245, upload-time = "2025-11-12T15:22:39.027Z" }, - { url = "https://files.pythonhosted.org/packages/81/c9/2628f408f0518b3bae49c95f5af3728b6ab498c8624ab1e03a43dd53d650/torch-2.9.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:19d144d6b3e29921f1fc70503e9f2fc572cde6a5115c0c0de2f7ca8b1483e8b6", size = 104134804, upload-time = "2025-11-12T15:22:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/28/fc/5bc91d6d831ae41bf6e9e6da6468f25330522e92347c9156eb3f1cb95956/torch-2.9.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:c432d04376f6d9767a9852ea0def7b47a7bbc8e7af3b16ac9cf9ce02b12851c9", size = 899747132, upload-time = "2025-11-12T15:23:36.068Z" }, - { url = "https://files.pythonhosted.org/packages/63/5d/e8d4e009e52b6b2cf1684bde2a6be157b96fb873732542fb2a9a99e85a83/torch-2.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:d187566a2cdc726fc80138c3cdb260970fab1c27e99f85452721f7759bbd554d", size = 110934845, upload-time = "2025-11-12T15:22:48.367Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b2/2d15a52516b2ea3f414643b8de68fa4cb220d3877ac8b1028c83dc8ca1c4/torch-2.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cb10896a1f7fedaddbccc2017ce6ca9ecaaf990f0973bdfcf405439750118d2c", size = 74823558, upload-time = "2025-11-12T15:22:43.392Z" }, - { url = "https://files.pythonhosted.org/packages/86/5c/5b2e5d84f5b9850cd1e71af07524d8cbb74cba19379800f1f9f7c997fc70/torch-2.9.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0a2bd769944991c74acf0c4ef23603b9c777fdf7637f115605a4b2d8023110c7", size = 104145788, upload-time = "2025-11-12T15:23:52.109Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8c/3da60787bcf70add986c4ad485993026ac0ca74f2fc21410bc4eb1bb7695/torch-2.9.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:07c8a9660bc9414c39cac530ac83b1fb1b679d7155824144a40a54f4a47bfa73", size = 899735500, upload-time = "2025-11-12T15:24:08.788Z" }, - { url = "https://files.pythonhosted.org/packages/db/2b/f7818f6ec88758dfd21da46b6cd46af9d1b3433e53ddbb19ad1e0da17f9b/torch-2.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c88d3299ddeb2b35dcc31753305612db485ab6f1823e37fb29451c8b2732b87e", size = 111163659, upload-time = "2025-11-12T15:23:20.009Z" }, -] - -[[package]] -name = "triton" -version = "3.5.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/6e/676ab5019b4dde8b9b7bab71245102fc02778ef3df48218b298686b9ffd6/triton-3.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fc53d849f879911ea13f4a877243afc513187bc7ee92d1f2c0f1ba3169e3c94", size = 170320692, upload-time = "2025-11-11T17:40:46.074Z" }, - { url = "https://files.pythonhosted.org/packages/b0/72/ec90c3519eaf168f22cb1757ad412f3a2add4782ad3a92861c9ad135d886/triton-3.5.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61413522a48add32302353fdbaaf92daaaab06f6b5e3229940d21b5207f47579", size = 170425802, upload-time = "2025-11-11T17:40:53.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/50/9a8358d3ef58162c0a415d173cfb45b67de60176e1024f71fbc4d24c0b6d/triton-3.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d2c6b915a03888ab931a9fd3e55ba36785e1fe70cbea0b40c6ef93b20fc85232", size = 170470207, upload-time = "2025-11-11T17:41:00.253Z" }, - { url = "https://files.pythonhosted.org/packages/27/46/8c3bbb5b0a19313f50edcaa363b599e5a1a5ac9683ead82b9b80fe497c8d/triton-3.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3f4346b6ebbd4fad18773f5ba839114f4826037c9f2f34e0148894cd5dd3dba", size = 170470410, upload-time = "2025-11-11T17:41:06.319Z" }, - { url = "https://files.pythonhosted.org/packages/37/92/e97fcc6b2c27cdb87ce5ee063d77f8f26f19f06916aa680464c8104ef0f6/triton-3.5.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0b4d2c70127fca6a23e247f9348b8adde979d2e7a20391bfbabaac6aebc7e6a8", size = 170579924, upload-time = "2025-11-11T17:41:12.455Z" }, - { url = "https://files.pythonhosted.org/packages/a4/e6/c595c35e5c50c4bc56a7bac96493dad321e9e29b953b526bbbe20f9911d0/triton-3.5.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0637b1efb1db599a8e9dc960d53ab6e4637db7d4ab6630a0974705d77b14b60", size = 170480488, upload-time = "2025-11-11T17:41:18.222Z" }, - { url = "https://files.pythonhosted.org/packages/16/b5/b0d3d8b901b6a04ca38df5e24c27e53afb15b93624d7fd7d658c7cd9352a/triton-3.5.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bac7f7d959ad0f48c0e97d6643a1cc0fd5786fe61cb1f83b537c6b2d54776478", size = 170582192, upload-time = "2025-11-11T17:41:23.963Z" }, -] - [[package]] name = "typer" version = "0.21.0" @@ -3878,18 +2507,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] -[[package]] -name = "typing-inspection" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - [[package]] name = "urllib3" version = "2.6.3" @@ -3899,21 +2516,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] -[[package]] -name = "virtualenv" -version = "20.36.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, -] - [[package]] name = "watchdog" version = "6.0.0" @@ -4049,99 +2651,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, ] -[[package]] -name = "wrapt" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/0d/12d8c803ed2ce4e5e7d5b9f5f602721f9dfef82c95959f3ce97fa584bb5c/wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd", size = 77481, upload-time = "2025-11-07T00:43:11.103Z" }, - { url = "https://files.pythonhosted.org/packages/05/3e/4364ebe221ebf2a44d9fc8695a19324692f7dd2795e64bd59090856ebf12/wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374", size = 60692, upload-time = "2025-11-07T00:43:13.697Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ff/ae2a210022b521f86a8ddcdd6058d137c051003812b0388a5e9a03d3fe10/wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489", size = 61574, upload-time = "2025-11-07T00:43:14.967Z" }, - { url = "https://files.pythonhosted.org/packages/c6/93/5cf92edd99617095592af919cb81d4bff61c5dbbb70d3c92099425a8ec34/wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31", size = 113688, upload-time = "2025-11-07T00:43:18.275Z" }, - { url = "https://files.pythonhosted.org/packages/a0/0a/e38fc0cee1f146c9fb266d8ef96ca39fb14a9eef165383004019aa53f88a/wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef", size = 115698, upload-time = "2025-11-07T00:43:19.407Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/bef44ea018b3925fb0bcbe9112715f665e4d5309bd945191da814c314fd1/wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013", size = 112096, upload-time = "2025-11-07T00:43:16.5Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0b/733a2376e413117e497aa1a5b1b78e8f3a28c0e9537d26569f67d724c7c5/wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38", size = 114878, upload-time = "2025-11-07T00:43:20.81Z" }, - { url = "https://files.pythonhosted.org/packages/da/03/d81dcb21bbf678fcda656495792b059f9d56677d119ca022169a12542bd0/wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1", size = 111298, upload-time = "2025-11-07T00:43:22.229Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d5/5e623040e8056e1108b787020d56b9be93dbbf083bf2324d42cde80f3a19/wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25", size = 113361, upload-time = "2025-11-07T00:43:24.301Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f3/de535ccecede6960e28c7b722e5744846258111d6c9f071aa7578ea37ad3/wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4", size = 58035, upload-time = "2025-11-07T00:43:28.96Z" }, - { url = "https://files.pythonhosted.org/packages/21/15/39d3ca5428a70032c2ec8b1f1c9d24c32e497e7ed81aed887a4998905fcc/wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45", size = 60383, upload-time = "2025-11-07T00:43:25.804Z" }, - { url = "https://files.pythonhosted.org/packages/43/c2/dfd23754b7f7a4dce07e08f4309c4e10a40046a83e9ae1800f2e6b18d7c1/wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7", size = 58894, upload-time = "2025-11-07T00:43:27.074Z" }, - { url = "https://files.pythonhosted.org/packages/98/60/553997acf3939079dab022e37b67b1904b5b0cc235503226898ba573b10c/wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590", size = 77480, upload-time = "2025-11-07T00:43:30.573Z" }, - { url = "https://files.pythonhosted.org/packages/2d/50/e5b3d30895d77c52105c6d5cbf94d5b38e2a3dd4a53d22d246670da98f7c/wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6", size = 60690, upload-time = "2025-11-07T00:43:31.594Z" }, - { url = "https://files.pythonhosted.org/packages/f0/40/660b2898703e5cbbb43db10cdefcc294274458c3ca4c68637c2b99371507/wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7", size = 61578, upload-time = "2025-11-07T00:43:32.918Z" }, - { url = "https://files.pythonhosted.org/packages/5b/36/825b44c8a10556957bc0c1d84c7b29a40e05fcf1873b6c40aa9dbe0bd972/wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28", size = 114115, upload-time = "2025-11-07T00:43:35.605Z" }, - { url = "https://files.pythonhosted.org/packages/83/73/0a5d14bb1599677304d3c613a55457d34c344e9b60eda8a737c2ead7619e/wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb", size = 116157, upload-time = "2025-11-07T00:43:37.058Z" }, - { url = "https://files.pythonhosted.org/packages/01/22/1c158fe763dbf0a119f985d945711d288994fe5514c0646ebe0eb18b016d/wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c", size = 112535, upload-time = "2025-11-07T00:43:34.138Z" }, - { url = "https://files.pythonhosted.org/packages/5c/28/4f16861af67d6de4eae9927799b559c20ebdd4fe432e89ea7fe6fcd9d709/wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16", size = 115404, upload-time = "2025-11-07T00:43:39.214Z" }, - { url = "https://files.pythonhosted.org/packages/a0/8b/7960122e625fad908f189b59c4aae2d50916eb4098b0fb2819c5a177414f/wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2", size = 111802, upload-time = "2025-11-07T00:43:40.476Z" }, - { url = "https://files.pythonhosted.org/packages/3e/73/7881eee5ac31132a713ab19a22c9e5f1f7365c8b1df50abba5d45b781312/wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd", size = 113837, upload-time = "2025-11-07T00:43:42.921Z" }, - { url = "https://files.pythonhosted.org/packages/45/00/9499a3d14e636d1f7089339f96c4409bbc7544d0889f12264efa25502ae8/wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be", size = 58028, upload-time = "2025-11-07T00:43:47.369Z" }, - { url = "https://files.pythonhosted.org/packages/70/5d/8f3d7eea52f22638748f74b102e38fdf88cb57d08ddeb7827c476a20b01b/wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b", size = 60385, upload-time = "2025-11-07T00:43:44.34Z" }, - { url = "https://files.pythonhosted.org/packages/14/e2/32195e57a8209003587bbbad44d5922f13e0ced2a493bb46ca882c5b123d/wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf", size = 58893, upload-time = "2025-11-07T00:43:46.161Z" }, - { url = "https://files.pythonhosted.org/packages/cb/73/8cb252858dc8254baa0ce58ce382858e3a1cf616acebc497cb13374c95c6/wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c", size = 78129, upload-time = "2025-11-07T00:43:48.852Z" }, - { url = "https://files.pythonhosted.org/packages/19/42/44a0db2108526ee6e17a5ab72478061158f34b08b793df251d9fbb9a7eb4/wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841", size = 61205, upload-time = "2025-11-07T00:43:50.402Z" }, - { url = "https://files.pythonhosted.org/packages/4d/8a/5b4b1e44b791c22046e90d9b175f9a7581a8cc7a0debbb930f81e6ae8e25/wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62", size = 61692, upload-time = "2025-11-07T00:43:51.678Z" }, - { url = "https://files.pythonhosted.org/packages/11/53/3e794346c39f462bcf1f58ac0487ff9bdad02f9b6d5ee2dc84c72e0243b2/wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf", size = 121492, upload-time = "2025-11-07T00:43:55.017Z" }, - { url = "https://files.pythonhosted.org/packages/c6/7e/10b7b0e8841e684c8ca76b462a9091c45d62e8f2de9c4b1390b690eadf16/wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9", size = 123064, upload-time = "2025-11-07T00:43:56.323Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d1/3c1e4321fc2f5ee7fd866b2d822aa89b84495f28676fd976c47327c5b6aa/wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b", size = 117403, upload-time = "2025-11-07T00:43:53.258Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b0/d2f0a413cf201c8c2466de08414a15420a25aa83f53e647b7255cc2fab5d/wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba", size = 121500, upload-time = "2025-11-07T00:43:57.468Z" }, - { url = "https://files.pythonhosted.org/packages/bd/45/bddb11d28ca39970a41ed48a26d210505120f925918592283369219f83cc/wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684", size = 116299, upload-time = "2025-11-07T00:43:58.877Z" }, - { url = "https://files.pythonhosted.org/packages/81/af/34ba6dd570ef7a534e7eec0c25e2615c355602c52aba59413411c025a0cb/wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb", size = 120622, upload-time = "2025-11-07T00:43:59.962Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3e/693a13b4146646fb03254636f8bafd20c621955d27d65b15de07ab886187/wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9", size = 58246, upload-time = "2025-11-07T00:44:03.169Z" }, - { url = "https://files.pythonhosted.org/packages/a7/36/715ec5076f925a6be95f37917b66ebbeaa1372d1862c2ccd7a751574b068/wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75", size = 60492, upload-time = "2025-11-07T00:44:01.027Z" }, - { url = "https://files.pythonhosted.org/packages/ef/3e/62451cd7d80f65cc125f2b426b25fbb6c514bf6f7011a0c3904fc8c8df90/wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b", size = 58987, upload-time = "2025-11-07T00:44:02.095Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fe/41af4c46b5e498c90fc87981ab2972fbd9f0bccda597adb99d3d3441b94b/wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9", size = 78132, upload-time = "2025-11-07T00:44:04.628Z" }, - { url = "https://files.pythonhosted.org/packages/1c/92/d68895a984a5ebbbfb175512b0c0aad872354a4a2484fbd5552e9f275316/wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f", size = 61211, upload-time = "2025-11-07T00:44:05.626Z" }, - { url = "https://files.pythonhosted.org/packages/e8/26/ba83dc5ae7cf5aa2b02364a3d9cf74374b86169906a1f3ade9a2d03cf21c/wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218", size = 61689, upload-time = "2025-11-07T00:44:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/cf/67/d7a7c276d874e5d26738c22444d466a3a64ed541f6ef35f740dbd865bab4/wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9", size = 121502, upload-time = "2025-11-07T00:44:09.557Z" }, - { url = "https://files.pythonhosted.org/packages/0f/6b/806dbf6dd9579556aab22fc92908a876636e250f063f71548a8660382184/wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c", size = 123110, upload-time = "2025-11-07T00:44:10.64Z" }, - { url = "https://files.pythonhosted.org/packages/e5/08/cdbb965fbe4c02c5233d185d070cabed2ecc1f1e47662854f95d77613f57/wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db", size = 117434, upload-time = "2025-11-07T00:44:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/6aae2ce39db4cb5216302fa2e9577ad74424dfbe315bd6669725569e048c/wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233", size = 121533, upload-time = "2025-11-07T00:44:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/79/35/565abf57559fbe0a9155c29879ff43ce8bd28d2ca61033a3a3dd67b70794/wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2", size = 116324, upload-time = "2025-11-07T00:44:13.28Z" }, - { url = "https://files.pythonhosted.org/packages/e1/e0/53ff5e76587822ee33e560ad55876d858e384158272cd9947abdd4ad42ca/wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b", size = 120627, upload-time = "2025-11-07T00:44:14.431Z" }, - { url = "https://files.pythonhosted.org/packages/7c/7b/38df30fd629fbd7612c407643c63e80e1c60bcc982e30ceeae163a9800e7/wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7", size = 58252, upload-time = "2025-11-07T00:44:17.814Z" }, - { url = "https://files.pythonhosted.org/packages/85/64/d3954e836ea67c4d3ad5285e5c8fd9d362fd0a189a2db622df457b0f4f6a/wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3", size = 60500, upload-time = "2025-11-07T00:44:15.561Z" }, - { url = "https://files.pythonhosted.org/packages/89/4e/3c8b99ac93527cfab7f116089db120fef16aac96e5f6cdb724ddf286086d/wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8", size = 58993, upload-time = "2025-11-07T00:44:16.65Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f4/eff2b7d711cae20d220780b9300faa05558660afb93f2ff5db61fe725b9a/wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3", size = 82028, upload-time = "2025-11-07T00:44:18.944Z" }, - { url = "https://files.pythonhosted.org/packages/0c/67/cb945563f66fd0f61a999339460d950f4735c69f18f0a87ca586319b1778/wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1", size = 62949, upload-time = "2025-11-07T00:44:20.074Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ca/f63e177f0bbe1e5cf5e8d9b74a286537cd709724384ff20860f8f6065904/wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d", size = 63681, upload-time = "2025-11-07T00:44:21.345Z" }, - { url = "https://files.pythonhosted.org/packages/39/a1/1b88fcd21fd835dca48b556daef750952e917a2794fa20c025489e2e1f0f/wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7", size = 152696, upload-time = "2025-11-07T00:44:24.318Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/d9185500c1960d9f5f77b9c0b890b7fc62282b53af7ad1b6bd779157f714/wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3", size = 158859, upload-time = "2025-11-07T00:44:25.494Z" }, - { url = "https://files.pythonhosted.org/packages/91/60/5d796ed0f481ec003220c7878a1d6894652efe089853a208ea0838c13086/wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b", size = 146068, upload-time = "2025-11-07T00:44:22.81Z" }, - { url = "https://files.pythonhosted.org/packages/04/f8/75282dd72f102ddbfba137e1e15ecba47b40acff32c08ae97edbf53f469e/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10", size = 155724, upload-time = "2025-11-07T00:44:26.634Z" }, - { url = "https://files.pythonhosted.org/packages/5a/27/fe39c51d1b344caebb4a6a9372157bdb8d25b194b3561b52c8ffc40ac7d1/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf", size = 144413, upload-time = "2025-11-07T00:44:27.939Z" }, - { url = "https://files.pythonhosted.org/packages/83/2b/9f6b643fe39d4505c7bf926d7c2595b7cb4b607c8c6b500e56c6b36ac238/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e", size = 150325, upload-time = "2025-11-07T00:44:29.29Z" }, - { url = "https://files.pythonhosted.org/packages/bb/b6/20ffcf2558596a7f58a2e69c89597128781f0b88e124bf5a4cadc05b8139/wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c", size = 59943, upload-time = "2025-11-07T00:44:33.211Z" }, - { url = "https://files.pythonhosted.org/packages/87/6a/0e56111cbb3320151eed5d3821ee1373be13e05b376ea0870711f18810c3/wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92", size = 63240, upload-time = "2025-11-07T00:44:30.935Z" }, - { url = "https://files.pythonhosted.org/packages/1d/54/5ab4c53ea1f7f7e5c3e7c1095db92932cc32fd62359d285486d00c2884c3/wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f", size = 60416, upload-time = "2025-11-07T00:44:32.002Z" }, - { url = "https://files.pythonhosted.org/packages/73/81/d08d83c102709258e7730d3cd25befd114c60e43ef3891d7e6877971c514/wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1", size = 78290, upload-time = "2025-11-07T00:44:34.691Z" }, - { url = "https://files.pythonhosted.org/packages/f6/14/393afba2abb65677f313aa680ff0981e829626fed39b6a7e3ec807487790/wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55", size = 61255, upload-time = "2025-11-07T00:44:35.762Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/a4a1f2fba205a9462e36e708ba37e5ac95f4987a0f1f8fd23f0bf1fc3b0f/wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0", size = 61797, upload-time = "2025-11-07T00:44:37.22Z" }, - { url = "https://files.pythonhosted.org/packages/12/db/99ba5c37cf1c4fad35349174f1e38bd8d992340afc1ff27f526729b98986/wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509", size = 120470, upload-time = "2025-11-07T00:44:39.425Z" }, - { url = "https://files.pythonhosted.org/packages/30/3f/a1c8d2411eb826d695fc3395a431757331582907a0ec59afce8fe8712473/wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1", size = 122851, upload-time = "2025-11-07T00:44:40.582Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8d/72c74a63f201768d6a04a8845c7976f86be6f5ff4d74996c272cefc8dafc/wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970", size = 117433, upload-time = "2025-11-07T00:44:38.313Z" }, - { url = "https://files.pythonhosted.org/packages/c7/5a/df37cf4042cb13b08256f8e27023e2f9b3d471d553376616591bb99bcb31/wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c", size = 121280, upload-time = "2025-11-07T00:44:41.69Z" }, - { url = "https://files.pythonhosted.org/packages/54/34/40d6bc89349f9931e1186ceb3e5fbd61d307fef814f09fbbac98ada6a0c8/wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41", size = 116343, upload-time = "2025-11-07T00:44:43.013Z" }, - { url = "https://files.pythonhosted.org/packages/70/66/81c3461adece09d20781dee17c2366fdf0cb8754738b521d221ca056d596/wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed", size = 119650, upload-time = "2025-11-07T00:44:44.523Z" }, - { url = "https://files.pythonhosted.org/packages/46/3a/d0146db8be8761a9e388cc9cc1c312b36d583950ec91696f19bbbb44af5a/wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0", size = 58701, upload-time = "2025-11-07T00:44:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/1a/38/5359da9af7d64554be63e9046164bd4d8ff289a2dd365677d25ba3342c08/wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c", size = 60947, upload-time = "2025-11-07T00:44:46.086Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3f/96db0619276a833842bf36343685fa04f987dd6e3037f314531a1e00492b/wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e", size = 59359, upload-time = "2025-11-07T00:44:47.164Z" }, - { url = "https://files.pythonhosted.org/packages/71/49/5f5d1e867bf2064bf3933bc6cf36ade23505f3902390e175e392173d36a2/wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b", size = 82031, upload-time = "2025-11-07T00:44:49.4Z" }, - { url = "https://files.pythonhosted.org/packages/2b/89/0009a218d88db66ceb83921e5685e820e2c61b59bbbb1324ba65342668bc/wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec", size = 62952, upload-time = "2025-11-07T00:44:50.74Z" }, - { url = "https://files.pythonhosted.org/packages/ae/18/9b968e920dd05d6e44bcc918a046d02afea0fb31b2f1c80ee4020f377cbe/wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa", size = 63688, upload-time = "2025-11-07T00:44:52.248Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7d/78bdcb75826725885d9ea26c49a03071b10c4c92da93edda612910f150e4/wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815", size = 152706, upload-time = "2025-11-07T00:44:54.613Z" }, - { url = "https://files.pythonhosted.org/packages/dd/77/cac1d46f47d32084a703df0d2d29d47e7eb2a7d19fa5cbca0e529ef57659/wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa", size = 158866, upload-time = "2025-11-07T00:44:55.79Z" }, - { url = "https://files.pythonhosted.org/packages/8a/11/b521406daa2421508903bf8d5e8b929216ec2af04839db31c0a2c525eee0/wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef", size = 146148, upload-time = "2025-11-07T00:44:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c0/340b272bed297baa7c9ce0c98ef7017d9c035a17a6a71dce3184b8382da2/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747", size = 155737, upload-time = "2025-11-07T00:44:56.971Z" }, - { url = "https://files.pythonhosted.org/packages/f3/93/bfcb1fb2bdf186e9c2883a4d1ab45ab099c79cbf8f4e70ea453811fa3ea7/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f", size = 144451, upload-time = "2025-11-07T00:44:58.515Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6b/dca504fb18d971139d232652656180e3bd57120e1193d9a5899c3c0b7cdd/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349", size = 150353, upload-time = "2025-11-07T00:44:59.753Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f6/a1de4bd3653afdf91d250ca5c721ee51195df2b61a4603d4b373aa804d1d/wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c", size = 60609, upload-time = "2025-11-07T00:45:03.315Z" }, - { url = "https://files.pythonhosted.org/packages/01/3a/07cd60a9d26fe73efead61c7830af975dfdba8537632d410462672e4432b/wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395", size = 64038, upload-time = "2025-11-07T00:45:00.948Z" }, - { url = "https://files.pythonhosted.org/packages/41/99/8a06b8e17dddbf321325ae4eb12465804120f699cd1b8a355718300c62da/wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad", size = 60634, upload-time = "2025-11-07T00:45:02.087Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, -] - [[package]] name = "xgboost" version = "3.1.3" @@ -4287,12 +2796,3 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] diff --git a/v1-sprints/001-scalar-tree-reference.md b/v1-sprints/001-scalar-tree-reference.md new file mode 100644 index 0000000..2f017e5 --- /dev/null +++ b/v1-sprints/001-scalar-tree-reference.md @@ -0,0 +1,86 @@ +# Sprint 001: Independent scalar/tree references + +Started: 2026-09-05. Starting revision: `9700845`. Status: **this sprint complete; F0.2 ongoing**. +Plan mapping: first part of B01/F0.2; A1/R1, scalar subset of C2/C3, D2; E1 reference preparation. + +## Purpose and scope + +Provide independent judging for F1 histogram/split/route/leaf/grow. The new implementation +and oracle must not share old production algorithms and repeat the same mistake. +Tests exhaustively enumerate and reduce original rows only on tiny fixtures. + +- Scalar weighted half-squared loss, base, Newton leaves and half gain; apply weight once. +- All fixed numeric-bin candidates, both missing routes, ties, infeasible candidates and actual children. +- D2 independent cohort information mass; total H alone is insufficient. +- Explicit topology and budgets for depthwise, best-first and symmetric growth; two-round squared-error traces. +- Isolated execution without production imports. Root `tests/conftest.py` imports old + openboost, so reference tests use `--confcutdir=tests/v1` and a clean subprocess import check. + +Not completed here: categorical/vector/other objectives, production trainers, persistence, +CUDA, real-data/baseline evaluation, or all F0.2. Other required cases remain required. + +## Execution checklist + +- [x] Read design, old split/plugin semantics and tests; identify doubled old gain and production imports. +- [x] Write hand-calculated tests and confirm failure before reference modules exist. +- [x] Implement independent scalar and brute-force tree functions in `tests/v1/reference/`. +- [x] Verify two rounds, three growth policies, missing/weight/cohort/ties/invalid inputs and import isolation. +- [x] Run focused tests and changed-file lint; review and commit. +- [x] Record reflection, results and handoff; update the index. + +## Acceptance + +1. For `[-2,-2,2,2]`, two feature bins and lambda=1, first leaves are ±4/3 and net gain=16/3. + With eta=.1, second-round residuals must change; hand calculations check both rounds. +2. Integer weights equal replicated rows with fixed bins. Zero weights cannot create splittable + nodes; invalid denominators and negative/non-finite values are rejected. +3. Both missing directions have optimal counterexamples; exact ties follow feature/candidate/missing order. +4. In D2's six-row fixture, unconstrained cut 1 becomes cut 2 with cohort constraints; no feasible candidate means no split. +5. Distinct fixtures exercise all three policies. Symmetric growth combines layer gains for a + common candidate before selecting; it cannot combine independently selected node winners. +6. References use only stdlib/NumPy, never OpenBoost objectives, splits, histograms or trainers. + They prepare E1; production parity still needs future components under test. + +## Execution and verification + +Deliverables: [scalar](../tests/v1/reference/scalar.py), [tree](../tests/v1/reference/tree.py), +[hand calculations and counterexamples](../tests/v1/test_tree_reference.py), +[isolation](../tests/v1/test_reference_independence.py), [instructions](../tests/v1/reference/README.md). + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache OPENBOOST_BACKEND=cpu uv run --no-sync pytest tests/v1 --confcutdir=tests/v1 -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check tests/v1 +``` + +- Red: two collection errors because `tests.v1.reference` did not exist. +- Green: **55 passed**, no skips; changed-file Ruff passed. +- Environment: local macOS CPU, Python 3.12.12, NumPy 2.3.5, pytest 9.0.2. +- A subprocess blocks all `openboost` imports; all three policies still produce two-round hand results. +- First gain=16/3; second leaves=±56/45; final raw=±58/225. D2 changes cut 1 to 2. +- This verifies reference fixtures, not new production parity or execution cost. + +## Reflection + +Opening observation: the architecture is concrete, but executable independent oracles are missing. +Evidence: `tests/v1/` does not exist; old tests use old gain/weight conventions and import production. +Decision: build a bounded scalar/tree reference first, without changing APIs or optimizing GPUs. +Next: implement against hand fixtures, then audit coverage and remaining F0.2 work. + +Closing observation: all policies share candidate/routing mathematics, but symmetric selection +must combine common candidates first. The left/right nodes prefer features 1/2 individually; +the symmetric oracle chooses common feature2, retaining the valid zero-gain left candidate. +Decision: public candidates must distinguish validity from gain; growth policy decides when +to discard nonpositive gain. + +Coverage: only numeric scalar references are complete. Categorical/binning, classification, +ranking, quantile/vector, positive/AFT, Normal/Formula and transaction/run work remain. +Fifty-five internal tests do not complete the foundation. During execution the user requested +retirement of all old production code and a clean v1 rebuild. Record/commit that separately, +preserving references and historical experiments without changing mathematical/quality gates. + +## Commits + +- `9700845`: prerequisite construction design. +- `e76a2cd`: sprint execution and reflection process. +- Implementation slice: `test: add independent scalar and tree references for v1`. +- `50acfc6`: verified independent references and 55 tests. diff --git a/v1-sprints/002-retire-legacy-production.md b/v1-sprints/002-retire-legacy-production.md new file mode 100644 index 0000000..a1efac5 --- /dev/null +++ b/v1-sprints/002-retire-legacy-production.md @@ -0,0 +1,70 @@ +# Sprint 002: Retire old production code and reset v1 implementation + +Date: 2026-09-05. Starting revision: `50acfc6`. Status: **this sprint complete; F0.2 ongoing**. +Trigger: the user explicitly selected retiring old production code and rebuilding v1. +This moves retirement forward from F5 at the user's request; F0.2/F0.3/F1 dependencies, +all required cases and acceptance gates remain unchanged. + +## Plan and boundaries + +1. Remove old trainers/models/core/backends/experimental/distributed implementations from + `src/openboost/`. Recreate only an under-construction namespace and typing marker, with no API shim. +2. Preserve `tests/v1/`, historical mathematical tests, raw benchmark artifacts, learnings and designs. + Default tests cover v1 only; historical tests require a fixed revision and are not current passes/skips. +3. Update README, package description/dependencies, test discovery, CI and documentation entry points. + Retired examples cannot advertise current APIs. Retire GPU/release workflows; an oracle-only namespace is not a releasable product. +4. Check imports/package contents, 55 reference tests, lint, builds and documentation; record reflection. + +## Acceptance + +- No old training/model/device modules remain; only the new namespace imports. +- Complete old sources remain at `50acfc6` and earlier; historical experiments and tests/v1 are preserved. +- Default tests and `pytest tests/` do not depend on old production; results describe a v1 subset only. +- Wheels exclude old modules; README/docs do not advertise removed runnable training APIs. +- Retirement adds no algorithm/performance claims and does not complete F1 or any E-gate. + +## Verification + +The old package had 47 tracked files: 46 Python modules and one typing marker. +After retirement, only recreated `__init__.py` and empty `py.typed` remain. Bytecode/Numba +caches were removed. Version became `1.0.0.dev0` so an empty namespace is not described as the old RC. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv lock --check --offline +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv build --offline +``` + +- **55 passed, no skips**. Default discovery excludes historical suites from the count. +- Ruff, lock check and strict docs passed; sdist and a wheel built from sdist succeeded. +- Wheel contains only two package files. `python -I` imports directly from it; no old + trainer/model/core/backend/distributed/experimental modules can be discovered. +- Fourteen changed Markdown files, 76 local links, fences and `git diff --check` passed. +- YAML structure checks passed for four workflows. Old GPU/release entry points retain only + explicitly failing manual status messages; no scheduled GPU or automatic release remains. + Docs workflow builds only `docs/v1/`, without deployment. +- `git diff HEAD -- benchmarks tests/v1` was empty: neither historical benchmarks nor committed references changed. +- Initial offline locking failed because cross-Python metadata was not cached. Online locking + succeeded, then offline checking passed with 96 resolved packages; no hashes were edited to bypass failure. +- These are local checks; GitHub CI matrix, CUDA, real quality and formal E-gates were not run. + +## Reflection + +The user wanted to leave the old production structure behind, rather than maintain a transition. +The 55 independent references were committed; old code still included global backends, old +weight/gain conventions, multiple model/experimental entry points and out-of-scope distributed code. +Decision: retire production as one slice, preserve counterexamples/evidence, and explicitly state +that there is temporarily no training API. Return to remaining F0.2 work; cleanup is not construction. + +After removal, all 55 references run unchanged. The reference diff is empty; isolated imports +and default tests pass. Preserve this independence. Future conformance requires separate +comparisons, not packaging the reference as an allegedly optimized product. Next: typed data, +binning/category/classification, then remaining mathematics and state/run probes. No application +implementation or E1/E3/E4 gate passed through retirement. + +## Commits + +- `50acfc6`: last revision containing full old production and v1 independent references. +- Retirement slice: `refactor: retire legacy production code for the v1 rebuild`. diff --git a/v1-sprints/003-data-classification-reference.md b/v1-sprints/003-data-classification-reference.md new file mode 100644 index 0000000..c3f356b --- /dev/null +++ b/v1-sprints/003-data-classification-reference.md @@ -0,0 +1,68 @@ +# Sprint 003: Independent training transforms and classification + +Starting revision: `cdce7d8`. Status: this sprint complete; F0.2 ongoing. +Scope: B01/F0.2, mathematical/data semantics subset of A1–A3/C1. + +## Plan + +1. Write failing examples for linear quantiles, minimum-value cuts, missing/unknown values, + nonordinal categories and label mapping. +2. Implement independent transforms and binary/softmax mathematics; check hand values, + finite differences, weights and two-round updates. +3. Record scope/results/reflection, run current v1 regression/lint and commit separately. + +## Acceptance and boundaries + +- Fit cuts/dictionaries on training only; validation cannot mutate state. Out-of-range values + use endpoint bins; reject infinity. +- Immutable column records with explicit missingness; stable dictionary one-vs-rest categories, + with unknowns following missing routing. +- Label mapping preserves probability-column meaning; unknown/missing labels fail. Reject + single-class binary training and require explicit initialization clipping. +- Stable binary loss/g/h; name exact softmax Hessian separately from tree diagonal upper bound. +- Apply weights only in loss aggregation/tree statistics, not prematurely in derivatives. + Round two uses every output direction from the new raw state. +- PreparedData identity, row-ID binding, persistence, full categorical growth and all F0.2 + remain incomplete. Production APIs, real data, CUDA and E-gates are not implemented. + +## Results and evaluation + +**Bounded sprint complete; F0.2 ongoing.** Added data.py/classification.py, 40 tests and +extended isolated execution blocking every `openboost` import. + +- Numeric hand values: `[0,2,4,6]` gives cuts 1.5/3/4.5. Repeated values retain a minimum-value + cut. Cases cover constant/all-missing columns, one bin, endpoints, NaN/infinity and extreme finite interpolation. +- Categories: stable training dictionary, unknown-as-missing and both missing routes. + Middle category m has one-vs-rest gain 64/15, strictly better than any ordinal threshold. +- Binary: at zero raw, g=±1/2 and h=1/4. Nonunit weights equal replicated rows; ±1000 loss + stays finite, and correctly directed raw=±100 retains a small nonzero loss without cancellation. + Initialization clipping must be explicitly supplied and representable. +- Softmax: hand-check K=3 exact Hessian and 2p(1-p) bound; finite differences check g/H. + Test shifts/class permutations, weights, invalid labels and range beyond float64. +- Two rounds: binary first leaves ±2/3; second leaves independently use p=sigmoid(-1/15). + Weighted softmax root first vector is (-3/11,0,3/11); all second directions use complete raw1. + A split multiclass tree is equivariant to class order over two rounds; train/validation raw match. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +**95 passed, no skips**; Ruff passed. Local macOS CPU/Python 3.12.12/NumPy 2.3.5/pytest9.0.2. +Before implementation, collection had two missing-module errors. No CUDA, real-data baseline, +persistence or formal E-gate ran; 95 tests do not mean 95 product features. + +## Reflection + +Binning/categories define candidates; classification output axes define geometry. These cannot +be hidden in a trainer. Minimum-value cuts, middle-category counterexamples and same-snapshot +softmax round two expose different errors. Preserve independent transform/output-schema/geometry +boundaries without introducing a universal trainer or production compatibility layer. +Immutable column state is not full PreparedData identity or typed Problem; category routing is +not complete categorical growth. Next prioritize ranking/quantile/vector leaf requirements, +then remaining binding, categorical growth, positive/AFT, Normal/Formula and state/run work. +All A1–A13 remain required; this provides future public-component conformance oracles. + +## Commits + +- This slice: `test: add independent data and classification references for v1`. diff --git a/v1-sprints/004-ranking-quantile-vector-reference.md b/v1-sprints/004-ranking-quantile-vector-reference.md new file mode 100644 index 0000000..91a91e1 --- /dev/null +++ b/v1-sprints/004-ranking-quantile-vector-reference.md @@ -0,0 +1,73 @@ +# Sprint 004: Ranking, quantile and vector-leaf references + +Starting revision: `e3d99ad`. Status: sprint complete; F0.2 ongoing. +Scope: B01/F0.2, A4/A5/A6 independent mathematics and two-round probes. + +## Plan and acceptance + +1. Write hand calculations/counterexamples for query pairs/normalization, left weighted + quantiles and shared split/vector leaves. +2. Implement NumPy references; verify finite differences, query shifts, round-two residuals, + K=1 reduction and output permutations. +3. Run regression/lint, reflect and commit while retaining all unfinished F0.2/v1 scope. + +- Ranking enumerates strict-relevance pairs within each query, averages by eligible pair count, + then applies query weight. Pair weight changes the numerator only; generic row weight is rejected. + Lambda weights freeze current ranking; ties use stable row IDs. NDCG=1 when IDCG=0. +- Quantile leaves use the left weighted quantile of routed residuals, not Newton values. + Pseudo h=1 is topology-only. Hand, nonsmooth optimality and two-round checks are required. +- A minimal shared vector stump tests summed output gain, full vector leaves and optional linear + split projection. Compare independent scalar trees. Train-only target scaling uses scale=1 for constants. +- A stump does not establish full vector growth, ranking sampling, real quality, persistence, + CUDA or a production foundation. E-gates remain unpassed. + +## Results and verification + +**Bounded sprint complete; F0.2 ongoing.** Three independent modules and 27 tests added, +including subprocess production-import blocking. **122 passed, no skips** in default v1 regression. + +- Ranking: one pair with query weight3 has g=(-1.5,1.5), h=(.75,.75); three pairs still average + to log(2). Ordinary-pair and frozen-lambda-surrogate finite differences pass. Cases cover + query shifts, gradient conservation, row-ID ties/permutations, zero IDCG, extreme scores, + no pairs/zero-weight queries, unknown weight keys and row-weight rejection. First tree leaves + are ±.4; round two recomputes from sigmoid(-.08). A new-ranking lambda-change case is retained. + No sampling or ordinary NDCG-gradient claim is made. +- Quantile: q=.1/.5/.9 hand values, left ties, positive-weight filtering, replication and pinball + optimality. Pseudo h=1 is not an exact Hessian. Scalar reference selects topology, then each + routed leaf solves residual/weight values. High leaves are8 then7.2; raw2=3.52. +- Vector: shared candidate gains sum across outputs, with independently solved K-dimensional + leaves. Projection uses gP and diag(PᵀHP), summing explicit sketch channels, not a full projected + Hessian. Identity projection is default. Projection changes topology only, never leaf width. + Shared fixture chooses feature1; projection to output1 chooses feature0; separate trees choose + different features. K=1 matches scalar depth1. Permutations, replication, root-only and two-round checks pass. +- TargetScale: unweighted per-column training population mean/std (ddof=0), constant scale=1, + tuple state. Mutating training inputs does not alter validation transform/inverse. Not a full output artifact. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +Ruff passed. Local macOS CPU/Python 3.12.12/NumPy 2.3.5/pytest9.0.2. Initial collection lacked the +ranking module. Initial implementation passed11/12; the remaining fixture assumption was wrong. +After retaining that counterexample, correcting the acceptance fixture and adding boundaries, all pass. +CUDA, real data, external methods, serialization, full-depth vector growth and E-gates are unverified. + +## Reflection: Counterexamples and closure + +Initial quantile fixture y=[0,2,10], w=[1,3,1], base=2 expected a high leaf8; it actually had no +split and update0. Under equality convention 1[y0; Tweedie y>=0 and 10, stable log-tail/Mills and distinct median/mean/survival/quantile units. +- Every objective checks new-raw gradients, leaves and predictions over two rounds. Persistence + and real external evaluation remain later work. +- Data probes report inconsistent/orphan/nonpositive payments. Missing payments are not automatically zero; + paid-count must pair with paid-mean. +- No product, real insurance/survival result or E-gate is complete; other cases remain required. + +## Results and evaluation + +Bounded scope complete. Added positive.py/survival.py, 61 tests and production-import isolation. +**183 passed, no skips**; Ruff passed. + +- Poisson hand base=log(7/5); effective all-zero counts require explicit minimum_rate. Doubling e + doubles count mean only, not rate. Weight remains separate. Gamma base is log(weighted mean). + Gamma/Tweedie loss/g/h match hand values and finite differences; geometry is unweighted. +- Independent algebra checks root leaves for two rounds of all three positive objectives; + integer weights match row-replicated losses. Invalid support/power/e/weight fail. Gamma log-ratio + retains valid geometry at y=1e308, F=log(y). Unrepresentable exponentials fail, with no hidden clipping/curvature floor. +- AFT event/right-censored derivatives pass finite differences. Lower must be finite positive; + upper equals lower or +inf, otherwise fail. Mixed two-round updates independently match erfc formulas. +- Normal tail: erfc/log1p for z<=8; 300-level continued fraction for z>8, retaining Mills-z correction + directly for curvature. Compare erfc over z=-10..30 and independent 64-point Gauss-Laguerre + integration at40/100; switching continuity passes. Never subtract SF to zero before logging or fake curvature by clipping. +- Outputs: separate median/mean/quantile/survival checks; survival decreases with time. Multiplying + time units by7 adds log(7) to event NLL through the density Jacobian, leaves censored NLL and both + gradients/Hessians unchanged. Mathematical inverse transforms do not replace serialization checks. +- A9 joins positive payments by policy ID and retains orphan/nonpositive/count-contradiction reasons. + Only zero-count policies without payments receive zero. paid-count/exposure×paid-mean equals + annualized amount; raw ClaimNb cannot replace paid-count. Entity ordering is irrelevant. + This is small-table mathematics, not real ETL or a trained model. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +Local macOS CPU/Python 3.12.12/NumPy 2.3.5/pytest9.0.2; no new dependencies. Missing-module tests +failed first; the first21 mathematical cases passed after implementation. An expanded check tried +to compare scalar loss and array g/h as one ragged array; it was corrected to compare each separately. +Lint naming issues were fixed. No real datasets/baselines, IPCW, CUDA, complete two-stage fit/save or E-gates ran. + +## Reflection: Direction after three implementation commits + +Sprints003–005 prepare different geometries, but production remains a namespace. All new code is +independent reference with production imports blocked; no performance, real quality or agent-cost +measurements exist. Test growth is not product value. These cases must become F1 component conformance +checks; F2 must measure algorithm-change cost. + +Exposure plays different roles in A7/A9, and events/censoring have different likelihoods at the same +time. Doubling exposure, paid-count products and time-Jacobian tests distinguish them. Preserve typed +target/offset/weight/output contracts; no generic trainer should guess semantics. +Next: Normal/NaturalBoost and Formula, then identity/state/run/full categorical/vector growth and +F0.3 freezing. Every A1–A13 still needs its own implementation and real evaluation; insurance/AFT +examples do not receive privileged scope. + +## Commits + +- This slice: `test: add positive target and AFT references for v1`. diff --git a/v1-sprints/006-normal-formula-reference.md b/v1-sprints/006-normal-formula-reference.md new file mode 100644 index 0000000..3829a3c --- /dev/null +++ b/v1-sprints/006-normal-formula-reference.md @@ -0,0 +1,65 @@ +# Sprint 006: Normal/NaturalBoost and Formula directions and commits + +Starting revision: `c5e9899`. Status: sprint complete; F0.2 ongoing. +B01/F0.2, independent A11/A12/D4 mathematics and candidate-state probes. + +## Plan and acceptance + +1. Hand/finite-difference checks for Normal ordinary gradients/Fisher/natural directions, + independent NLL/CRPS, and Formula softplus/Jacobian/GGN/ordinary-diagonal-full directions. +2. Fit negative directions using independent scalar trees; apply fit weight once. Check two-round + joint/ordered updates, fixed steps and bounded backtracking. Rejection must preserve raw/terms. +3. Repeated Z with different x, single-x nonidentifiability and misspecification counterexamples; + full regression/lint/reflection/commit. + +These are mathematical/minimal update references, not production runtime or full transactions. +Fisher is not a Hessian; GGN does not establish identifiability. Initialization cannot read validation. +Persistence, real quality, agent costs, CUDA and E-gates remain pending; all A1–A13 remain required. + +## Results and verification + +Added coupled.py, 25 tests and import isolation. **208 passed, no skips**; Ruff passed. +Local macOS CPU/Python 3.12.12/NumPy 2.3.5. + +- Normal: hand Fisher/natural direction, weighted training mean/scale and explicit scale floor. + Independent evaluator does not call the objective; closed-form NLL/CRPS checked separately. + Two-round natural root leaves match per-round mu/sigma algebra. Nonunit fit weights equal replication. +- Formula: stable softplus/expm1, analytic Jacobian matches finite differences, GGN=JᵀJ. All three + directions have two-round joint checks. Full uses explicit2×2 inverse and rejects undamped + rank-deficient GGN. Ordinary direction does not use metric/damping as a preconditioner. +- Each direction-tree leaf matches an independent sum of same-snapshot negative directions. + Predictions rebuild from tree/channel/coefficient. Z/x are separate, with repeated Z/different x + and known-a/b generated fixtures. +- Two-round ordered Normal recomputes geometry after each parameter and differs from joint log-scale. + Explicit fixed finite steps can allow higher loss; backtracking requires strict decrease. + Fixed steps still reject invalid geometry. +- Backtracking rejects invalid step1000 and finite-but-worse step5, then accepts.1; this equals direct.1. + Full rejection leaves no terms and raw_before=raw_after. Caller mutation cannot alter snapshots. +- Two a/b pairs at one x give the same output: full GGN is not identifiability. A decreasing target + for one recipe contradicts a saturating increasing formula. No real Concrete parameter-recovery claim. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +Missing-module collection failed first; the first12 mathematical cases passed. Lint formatting was +fixed; review added explicit rejection of softplus underflow to0 without changing positive support. +No dependencies added. Full persistence/best/early-stop, production CPU, real NLL/CRPS/Formula quality, +CUDA and E-gates remain unverified. This step is a small reference, not a universal trainer/runtime. + +## Reflection + +Direction, direction fitting and acceptance can be expressed separately; Normal/Formula need not +be forced into scalar Newton interfaces. Both reuse scalar trees, solve Fisher/GGN before fitting, +apply fit weights once and use original loss for acceptance. Preserve direction-adapter/transaction boundaries. + +A1–A12 mathematical probes still do not complete F0.2. Missing items include A13 isolation/selection, +row binding, full categorical/vector growth, D3 penalized leaves and full composition/persistence +references. Next prioritize state/run/identity and an acceptance ledger. Do not optimize production +prematurely or endlessly add similar mathematical tests. Freeze F0.3 before F1 public components; +all A1–A13 still need production and real evaluation. + +## Commits + +- This slice: `test: add Normal and Formula directional update references for v1`. diff --git a/v1-sprints/007-identity-runs-reference.md b/v1-sprints/007-identity-runs-reference.md new file mode 100644 index 0000000..1f62482 --- /dev/null +++ b/v1-sprints/007-identity-runs-reference.md @@ -0,0 +1,66 @@ +# Sprint 007: Identity, run isolation and model selection + +Starting revision: `c0e0b2c`. Status: sprint complete; F0.2 ongoing. +B01/F0.2, independent A13/D5/C1/C4 references. + +## Plan and acceptance + +1. Typed content hashes and row-ID binding: equal shapes do not permit reuse across different + contents/folds/schema/cuts/dictionaries. Target/weight/offset belong to Problem identity, not only prepared data. +2. Sequential runs fit actual small scalar/vector squared-loss trees with independent budgets, + seeds, best and early stopping. Stable sampling keys make independent/sequential/reordered/regrouped + results equal. Preserve failures without contaminating other runs. +3. Select completed runs by validation only; stored best terms reconstruct raw. Pin an RNG fixture, + audit F0.2 gaps, run regression/lint, reflect and commit. + +Hashes are semantic references, not production PreparedData, persistence or security signatures. +Regrouping simulates sequential scheduling, not batched GPU execution; no speed/memory claim. +Injected failures test handling; actual required failures cannot count as passes. E-gates remain incomplete. + +## Results and verification + +Added runs.py, 21 tests: **229 passed, no skips**, Ruff passed. Import-isolated subprocess executes +an actual run and a fixed-seed assertion. + +- DataIdentity stores typed SHA256 plus immutable row IDs, covering values/order/schema/transformer + version/cuts/dictionaries. NaNs normalize; None/string/int/float remain distinct; mapping key order + is irrelevant. Binding checks prepared order and every named role; hashes do not validate target support. +- Review found digest-only identities cannot validate redeclared row order. Store row IDs too; + reversing all fields together still cannot bind to the original prepared ordering. +- Real squared-loss trees have separate K=1/K=2 raw/budgets and joint output commits per round. + M=1/8/32 preserve all records. Independent/sequential/reverse/regrouped results match exactly; + regrouping is not parallelism. +- Best changes only on strict validation improvement; ties retain earlier rounds. Base is round0. + Independent patience works; harmful training restores base. Stored terms/coefficients reconstruct best_raw. +- Failed runs keep errors/completed rounds while others remain unchanged. Same-ID retry uses the + same first sample. Invalid config/no problem/all-failed cases are explicit; another available model + cannot erase a required failure. +- RNG uses first64 bits of SHA256 over typed UTF-8 JSON key(seed, run_id, round, component, purpose), + excluding attempt/scheduler position. `(7,'run-α',2,'tree','rows')` → `6175955064790668999`. + PCG64 is a CPU reference, not a cross-backend/future-NumPy bitwise promise. +- Selection reads successful validation-best records only. Different Problem IDs cannot be mixed + even when losses are numeric. Same-problem ties use run ID; heterogeneous execution does not imply comparable metrics. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +Local macOS CPU/Python 3.12.12/NumPy 2.3.5/pytest9.0.2. Missing-module collection failed first; +first7 cases passed, then boundaries/fixed keys/M counts and lint were completed. No GPU, +production cache/runtime, real selection cost, persistence or E-gates ran. Run recipe is unit-weight +squared loss only; general binding of weight/offset roles does not mean other recipes were executed. + +## Reflection + +K versus M and prepared versus Problem identity affect correctness beyond shapes. Heterogeneous K +runs stay isolated; changed targets/validation change Problem IDs and reject incomparable selection; +uniformly reversed fields still violate prepared order. Public foundation must retain explicit +input/target/output contracts. Scheduling executes; it cannot guess metric comparability. +Next: [F0.2 ledger](f0-2-acceptance-ledger.md) tracks exact D1/D3/D4 fixtures, full categorical/vector +growth and finite compositions. Fill those before closing F0.2 and starting F0.3. The229 tests are +not v1 product completion, nor a reason to keep adding the same mathematical tests indefinitely. + +## Commits + +- This slice: `test: add identity and isolated run references for v1`. diff --git a/v1-sprints/008-author-mutation-reference.md b/v1-sprints/008-author-mutation-reference.md new file mode 100644 index 0000000..8839c1d --- /dev/null +++ b/v1-sprints/008-author-mutation-reference.md @@ -0,0 +1,63 @@ +# Sprint 008: Exact D1/D3/D4 author-task references + +Starting revision: `6ae1960`. Status: sprint complete; F0.2 ongoing. +B01/F0.2 author development references; not an E5 author evaluation. + +## Plan and acceptance + +1. D1: tau=.8 expectile, weighted base, positive/negative/zero residuals, tau=.5 reduction and two rounds. +2. D3: minimize sum(w*pinball)+lambda*(v-anchor)^2/2 by residual breakpoints and interval stationary + points; compare independent subgradients/optimality, lambda/anchor and two-round routed leaves. + Do not normalize the weight sum. +3. D4: ordered Normal parameters; alpha=.1*.5^j, j=0..5. Commit only finite strict decrease. + Reverse/NaN trials reject all six; parameter2 reads accepted or unchanged state. Reuse immutable Update. +4. Full regression/lint, acceptance ledger, reflection and commit. + +Public API extensions, real author time, persistence and full runtime/best/RNG integration remain +later work. Solving development tasks provides oracles, not E5 success. + +## Results and verification + +Added author.py, 26 tests and import isolation. **255 passed, no skips**; Ruff passed. +Local macOS CPU/Python 3.12.12/NumPy 2.3.5. + +- D1: residual-sign gradient/Hessian; at r=0 choose h=2*tau explicitly, not a unique classical + second derivative. tau=.5 reduces to half-square. For[0,2], tau=.8 base=1.6; weights[3,1] give8/7. + Independently enumerate fixed-weight stationary points between targets. Check both-sign finite + differences, zero-weight outliers, replication, constants, two-round predictions and hand leaves. +- D3: sum(w*pinball) is unnormalized. Enumerate all residual breakpoints and stationary points of + every open interval, including tails. [0,2,10]/[1,3,1]/q=.5/anchor0 gives v=1.5 at lambda1, + .15 at10, and 2 at.1. Independent subgradients contain0; nine q/anchor grids check global minima. + Replication matches weighting; scaling only weights changes the solution, scaling lambda too preserves it. +- Routed D3 trees replace terminal leaves only, rereading original residuals/weights. High leaves + are8/7.2, low leaves-2/-1.8; raw2=[1.62,1.62,3.52]. Test distinction from ordinary quantiles separately. +- D4 updates0→1, each with at most six steps(.1,.05,.025,.0125,.00625,.003125). Reversed/NaN + directions reject all six without terms/raw/loss residue. Parameter2 reads accepted state after + success or original state after rejection, across two rounds. Input mutation cannot alter old + snapshots; deterministic fixtures do not consume global RNG. Full best/per-run RNG remains integration work. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +Initial missing-module failure; first15 cases passed after implementation, then boundary/weight +checks completed. No public author extensions, competitor-cost experiments, real tasks, CUDA or +persistence ran. These are not E5 results. + +## Reflection: Converging after three implementation commits + +D1/D3/D4 now have falsifiable references rather than vague custom objective/leaf/update claims. +Expectile changes geometry, penalized quantiles can optimize between breakpoints, and ordered +policies depend on accepted state. They reuse trees without sharing incorrect mathematical assumptions. +F1 should expose objective, leaf solver and update-policy replacement; F2 measures actual change cost. + +The last three sprints filled direction/run/development-task mathematics, not production. +Only tests/v1/reference changed; F0.3 data/budgets/judge are not frozen. Converge on the ledger, +without expanding development tasks or adding ungated references. Next: full categorical/multilevel +vector growth, raw transforms, offset/two-stage compositions, then F0.2 exit and F0.3. F1–F5 and +all A1–A13 remain required; do not mark E-gates green early. + +## Commits + +- This slice: `test: add exact author mutation references for v1`. diff --git a/v1-sprints/009-mixed-vector-growth-reference.md b/v1-sprints/009-mixed-vector-growth-reference.md new file mode 100644 index 0000000..1947999 --- /dev/null +++ b/v1-sprints/009-mixed-vector-growth-reference.md @@ -0,0 +1,65 @@ +# Sprint 009: Mixed features and multilevel vector trees + +Starting revision: `a6b3eeb`. Status: sprint complete; F0.2 ongoing. +B01/F0.2, A2/A6/C1–C3 reference integration gaps. + +## Plan and acceptance + +1. Fit numeric/category transformers on training; trees retain them for raw validation prediction. +2. Independently enumerate original rows for multilevel scalar/vector depthwise/best-first/symmetric + trees. Categories use one-vs-rest equality, both missing directions; projected splits retain full leaf statistics. +3. Two-round mixed binary and multioutput tasks; compare K=1 against scalar oracle, check row conservation, + unknown/missing, output permutations, replication, no legal split and immutable snapshots. +4. Full regression/lint, ledger and reflection; offset/two-stage models belong to the next finite slice. + +No production API/GPU path. Reference outputs are[N, K]; internal layout is not a production constraint. +Keep the existing structurally different scalar oracle rather than replacing it to obtain self-consistency. + +## Results and verification + +Added mixed.py, 24 tests and import isolation. **279 passed, no skips**. Training transforms, +full scalar/vector trees and raw prediction connect in the same fixtures. + +- Transformer fits training X only, retaining names/kinds/cuts/category dictionaries. Predict reuses + it; unknown follows missing. Identity binds original contents, row IDs and fitted transform state. +- Growth reroutes/reduces original rows for every candidate. All three policies support multiple + levels. Category equality and numeric thresholds require no histogram/backend. No leaf-budget + option exists: this reference stops at max_depth, not a claim that all growth options are covered. +- Candidate gain sums split channels; explicit gP/diag(PᵀHP) projection retains full K leaves. + Three-policy two-round four-leaf fixtures map one row per leaf: predictions y/2,.95*y/2, + raw2=.0975*y. K=1 matches scalar on numeric/missing; permutations and replication pass. +- Mixed numeric/category three-level tree yields six single-row leaves and exact vectors at lambda0. + Symmetric checks common per-level conditions. Original scalar/stump references remain unchanged comparisons. +- Two-round mixed binary joins gradients/trees/raw predictions. Out-of-range numeric, unseen categories + and missing values do not refit cuts/dictionaries. Middle category m is isolated; unknown follows missing. +- Both missing directions, zero-weight/all-missing/zero-curvature rows, invalid schema/projection/depth, + input mutation, row conservation and no duplicate routing are covered. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +``` + +Local macOS CPU/Python 3.12.12/NumPy 2.3.5. Missing-module failure first; initial8/11 passed. +Three failures reflected the incorrect expectation below. Preserve the counterexample and use +positive-gain multilevel fixtures. No CUDA, real quality, production API or persistence results. +Transformer is an immutable reference record, not a production layout/serialization format. + +## Reflection: Regularization cost of vector splits + +Initially first-axis±1/second-axis±3 targets were expected to split again, but all policies stopped +at one level. Second-level first-axis gain is+.5; repeated regularization of constant second-axis +leaves costs-1.5, totaling-1. This follows summed-output gain, not a grow bug. Keep the no-more-split +test and use first-axis±2 for positive second-level gain and full two-round checks; do not lower thresholds. + +Categorical/vector references now connect training transforms to raw prediction with independent +scalar/stump comparisons. Those F0.2 gaps are covered, not C1/C2/C3 production conformance. +Next complete offset/two-stage and best/per-run RNG compositions, audit remaining F0.2, then F0.3. +Avoid endlessly redoing completed mathematics in isolated fixtures. All A1–A13 remain required. + +## Commits + +- This slice: `test: add mixed feature and full vector growth references for v1`. + +Precommit review: finite node scores do not guarantee a finite sum of child gains. Add an overflow +counterexample and explicit candidate/layer-total checks so infinity cannot win. Final count: 24 new, 279 passed. diff --git a/v1-sprints/010-reference-integration-exit.md b/v1-sprints/010-reference-integration-exit.md new file mode 100644 index 0000000..3bae2e3 --- /dev/null +++ b/v1-sprints/010-reference-integration-exit.md @@ -0,0 +1,50 @@ +# Sprint 010: Finite compositions and F0.2 exit audit + +Starting revision: `972f80a`. Status: complete. Fill A5/A7/A9/C4 reference compositions and audit F0.2 exit. + +## Plan and acceptance + +1. Immutable scalar ensembles retain base/terms/coefficients. Poisson raw excludes offsets; + check two rounds and new exposure. Actually fit paid-count rate×Gamma severity and three parallel quantile models. +2. Connect ordered Normal updates to versioned proposals, train/validation caches and best restoration. + Reject stale parents/NaN validation without state changes. Restore matching tree/coefficients/cache/step. + Derive RNG from run ID/seed/logical step; retries cannot consume future keys. +3. Regression/lint and A1–A13/D1–D5 reference audit. Close only evidenced stages and identify F0.3/F1 follow-ups. + +No serialization/public runtime or real quality/speed/author-cost evaluation; those remain F1/F0.3–F4. +Compositions may reuse references, never production imports or call memory restoration an artifact round trip. + +## Results and verification + +Nine new checks; 288 passed, no skips. Poisson raw remains log-rate over two rounds; new exposure +changes count mean only. Paid-count joins feed actual reference Poisson/Gamma fits; ClaimNb cannot +replace positive-payment counts. Three quantiles retain/reconstruct base, trees and coefficients. +Four ordered Normal commits across two rounds use versioned state. Base and nonzero best snapshots +restore all terms, both caches and logical step; fresh versions reject old proposals. +Reverse-direction rejection, non-finite validation, foreign runs, corrupted raw and broadcastable +row/shape errors leave original state intact. Step-derived keys survive retry/restoration; no device/global RNG state exists here. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost tests/v1 tests/conftest.py +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict +``` + +Local macOS/Python 3.12.12/NumPy 2.3.5; tests/lint/strict docs pass. Isolated reference compositions +run while every openboost import is blocked. See the [F0.2 ledger](f0-2-acceptance-ledger.md). + +## Reflection: End mathematical preparation and advance the evaluable foundation + +Independent mathematics, counterexamples and finite two-round compositions now cover A1–A13/D1–D5. +Close F0.2 preparation instead of indefinitely adding isolated fixtures and delaying the product. +No production conformance, persistence, real quality, author-cost or GPU gate closes with it. +Review corrected the nonzero-best fixture: initialization and updates must use the same metric +for meaningful comparisons. Explicit shape checks are also needed; allclose does not prevent broadcasting. + +Next F0.3: real manifests/hashes, capability smoke, budgets, held-out tasks and judge, retaining +every required case. F1 builds public components checked against these oracles. Reference Track +is a finite Normal-state probe, not a production transaction protocol or disk checkpoint. + +## Commits + +- This slice: `test: integrate reference models and close v1 F0.2`. diff --git a/v1-sprints/011-artifact-integrity-judge.md b/v1-sprints/011-artifact-integrity-judge.md new file mode 100644 index 0000000..05f421c --- /dev/null +++ b/v1-sprints/011-artifact-integrity-judge.md @@ -0,0 +1,49 @@ +# Sprint 011: F0.3 evaluation artifact integrity judge + +Starting revision: `bc68ab9`. Status: complete for the F0.3 integrity subset only. + +## Plan and acceptance + +1. Create versioned `benchmarks/v1/` integrity contracts: expected cells, run identity, cache keys + and artifact hashes. Smallest failure: a missing required fold fails even if all remaining cases say pass. +2. Offline judge/CLI with strict JSON and counterexamples for missing/duplicate/unknown cases, + cache pollution, NaN, wrong backend, worker error, timeout, unrun GPU and missing/corrupt predictions. + Synthetic artifacts are test inputs, not frozen real data. +3. Full regression/lint/docs, scope record and commit. Output integrity_pass only, never an E-gate pass. + +No training, real quality evaluator or invented data/library/resource freezes. F0.3 remains incomplete; +next slices must advance real data and baseline preparation. + +## Results and acceptance + +Added48 tests; 336 total. Cases cover a missing second fold with all A-IDs present, duplicate/unknown +cases, eight changed cache inputs, non-finite metrics/predictions, false passes, nonzero workers, +missing/corrupt files, path/symlink escape, optional unsupported GPU and CLI exit codes. +Every A-ID needs a required CPU cell, but that structural check is not a sufficient experimental design. +Every declared cell needs a record; optional failures remain visible. Only integrity_pass can pass; +gate_results stays empty. + +Cache keys bind the complete manifest and cell. Only dirty=false is supported because a boolean +cannot identify uncommitted patches; dirty runs need content digests. No provenance authenticity +claim is made. Rectangular finite prediction JSON is checked, not target alignment/units or recomputed metrics. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict +``` + +Local macOS CPU/Python 3.12.12; no real training, CUDA or quality result. Manifests are explicitly temporary synthetic inputs. + +## Reflection + +Mathematical references exist, but trusting producer pass records cannot exclude missing folds, +stale caches or worker failures. Build an independent integrity check with distinct E-gate fields. +Initial collection failed on the absent module; counterexamples passed after implementation. +Review added a missing second A1 fold: deleting the only A13 record alone did not test repeated folds. +Next pursue real sources/licenses/hashes and split adapters; schema growth is not real evaluation progress. +All A1–A13 remain required. Budgets, baseline capabilities, held-out tasks and full judge remain unfrozen. + +## Commits + +- This slice: `test: add v1 artifact integrity judge`. diff --git a/v1-sprints/012-bike-data-freeze.md b/v1-sprints/012-bike-data-freeze.md new file mode 100644 index 0000000..af86a39 --- /dev/null +++ b/v1-sprints/012-bike-data-freeze.md @@ -0,0 +1,52 @@ +# Sprint 012: A5 Bike Sharing data and date splits + +Starting revision: `e2f2c6b`. Status: complete for A5 data preparation only. + +## Plan and acceptance + +1. Fetch the UCI ZIP and record license, archive/member hashes and actual rows. Read hour.csv only, + allow calendar predictors, exclude measured weather, casual/registered, instant and date from X. + Original instant is a row ID only. +2. Rolling origins train on the first50/55/60/65/70% of full dates, followed by10% validation and 10% test. + Endpoints are floor(D*p/100); freeze dates/counts/row-ID hashes. Smallest failure: dates cannot + cross partitions; weather/component counts cannot affect X. +3. Verify pinned archives with a reproducible CLI record; counterexamples, regression/lint, learning and commit. + +No model training/evaluation or test-driven protocol choice. This is A5 only; all other required cases remain. + +## Results and evidence + +[Freeze](../benchmarks/v1/datasets/bike.json) records archive/member/X/y/row-ID hashes, five windows' +dates/counts/hashes and provenance. Actual data: 17,379 rows, 731 dates, 7 calendar features. UCI's page +says17,389; retain the discrepancy rather than overriding parsed bytes. adapter_sha256 identifies +source; parent revision and dirty=true are honest. This is not an integrity-v0 run manifest or a full training evaluation. + +Window train/validation/test row counts: 8645/1744/1750,9529/1746/1752,10389/1750/1752, +11275/1752/1752,12139/1752/1752. Dates/IDs are disjoint within windows, overlapping across rolling +origins; do not interpret them as independent random folds. Use floor over731 dates, not row percentages; +do not synthesize missing hours. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.bike /tmp/openboost-v1-bike.zip --verify benchmarks/v1/datasets/bike.json +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict +``` + +Real archive replay matched all data fields; corrupting a frozen test count made CLI exit nonzero. +Added24 tests; 360 passed, no skips. Cases cover full dates, uneven hours, leaking fields, invalid +calendar/count, duplicate IDs/timestamps, corrupted archives and adapter source identity. +macOS/Python 3.12.12/NumPy 2.3.5. + +## Reflection + +Source-page row counts differ from actual files, and hourly records per date vary. Use byte hashes +and parsed data, not webpage metadata or row percentages as split truth. Sandboxed curl DNS failed; +authorized download succeeded. Initial tests lacked the module; later parametrized cases needed +pytest reimported after earlier unused-import cleanup. Final regression passed. +Next: remaining datasets, starting with A1/A11 Housing archive hashes and seeds 3–4; then other +required data/baseline capabilities/budgets. A5 has no model result and F0.3 remains open. + +## Commits + +- This slice: `data: freeze A5 bike sharing inputs and rolling splits`. diff --git a/v1-sprints/013-housing-five-splits.md b/v1-sprints/013-housing-five-splits.md new file mode 100644 index 0000000..67980b2 --- /dev/null +++ b/v1-sprints/013-housing-five-splits.md @@ -0,0 +1,46 @@ +# Sprint 013: Five Housing splits for A1/A11 + +Starting revision: `dd2e9ad`. Status: data-hash/five-split subset complete; license unresolved. + +## Plan and acceptance + +1. Verify the cached archive against old hashes; independently implement column mapping, + household ratios and target units. A hand-calculated row catches ratio/column mistakes. +2. Match combined X/y hash and old seeds 0–2; add3–4 and member/all-split hashes. Check completeness, + disjointness, RNG isolation, invalid inputs and float32 overflow. +3. Real replay, regression/lint/docs and commit. A1/A11 share data but require separate quality results. + +No model/GPU training. If source licensing is not verified, record unresolved rather than inventing a label. + +## Results and acceptance + +Cached archive matches fixed SHA256. The independent nine-column adapter computes AveRooms, +AveBedrms and AveOccup using households, preserving100,000 USD targets and little-endian float32. +The20640×8 combined X/y hash and all nine old split hashes match; six new hashes cover seeds 3–4. +[Freeze](../benchmarks/v1/datasets/housing.json) stores member hash, five splits, source and environment. +Each partition is12384/4128/4128 and internally complete/disjoint. Random splits do not establish geographic generalization. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.housing build/foundation_data/cal_housing.tgz --verify benchmarks/v1/datasets/housing.json +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict +``` + +Real replay matches; corrupted seed 4 makes CLI exit nonzero. Added20 tests; 380 passed, no skips. +Invalid households/counts/shapes/float32 overflow fail. Hand ratios, no cross-row learning and +unchanged global RNG pass. Local macOS/Python 3.12.12/NumPy 2.3.5. Raw data is not in Git; +no models or test metrics were run. + +## Reflection + +Historical records are reusable but have only three seeds, and the archive lacks license details. +Preserve hash semantics, verify independently and add two seeds. Public availability does not +establish a particular license. A1 and A11 need separate RMSE and NLL/CRPS results, not two source counts. +Initial missing-module collection failed; an early test-line semicolon was fixed by formatting. +Next Adult categories/official-test splits, then other required datasets. Licensing, capabilities, +budgets, held-out tasks and the full runner remain pending; F0.3 stays open. + +## Commits + +- This slice: `data: freeze A1 A11 housing with five splits`. diff --git a/v1-sprints/014-adult-data-freeze.md b/v1-sprints/014-adult-data-freeze.md new file mode 100644 index 0000000..1f7e4c6 --- /dev/null +++ b/v1-sprints/014-adult-data-freeze.md @@ -0,0 +1,52 @@ +# Sprint 014: Adult official test and stratified splits + +Starting revision: `f74df58`. Status: complete for A2 raw data preparation. +The user reaffirmed evaluation-first execution: retain F0.3 before F1, with no +premature switch to production GPU implementation. + +## Plan and acceptance + +1. Pin UCI archive and adult.data/adult.test hashes. Preserve official test, exclude + fnlwgt from 13 features, map categorical `?` to None, strip exactly one test-label + suffix dot, and use unit weights. +2. For seeds 0–4, use default_rng(seed), permute class 0 then class 1, take + floor(0.8*n_class) for training and the remainder for validation. Sort returned + source indices. Freeze source row IDs, hashes and class counts. +3. Replay real data, test without network, run regression/lint, document and commit. + Smallest counterexample: changing fnlwgt cannot change X or sample weights. + +## Results and verification + +The archive contains 32,561 official training and 16,281 official test records. +All five test partitions retain identical row IDs. The adapter preserves missing +categories and source-qualified physical-line IDs; no category vocabulary is learned. +Duplicate predictor rows are counted in the artifact, not silently deleted or +interpreted as proof of repeated people. No person identifiers are available. + +[The frozen record](../benchmarks/v1/datasets/adult.json) binds raw member hashes, +parsed records, five splits, source bytes and execution provenance. Real archive +replay matched. Sixteen tests cover exclusions/missingness, strict labels, category +separation, class proportions, invalid inputs, RNG isolation and archive identity. + +```bash +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync python -m benchmarks.v1.adult /tmp/openboost-v1-adult.zip --verify benchmarks/v1/datasets/adult.json +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py +UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict +``` + +Local macOS/Python3.12.12/NumPy2.3.5. No models trained or test metrics inspected. +Numeric encoding, baseline capabilities, budgets and quality evaluation remain pending. + +## Reflection + +The fixed official test must not be recombined with training before splitting. +The numeric population weight column is not automatically a sample weight; removing +it from X is an explicit task contract. Repeated predictor values cannot establish +entity identity. The initial test failed because the adapter did not yet exist. +The user additionally requires English throughout the repository; existing prose +will be translated separately while preserving all specifications and evidence. + +## Commits + +- This slice: `data: freeze Adult official test and stratified splits`. diff --git a/v1-sprints/015-english-repository.md b/v1-sprints/015-english-repository.md new file mode 100644 index 0000000..9e65d83 --- /dev/null +++ b/v1-sprints/015-english-repository.md @@ -0,0 +1,60 @@ +# Sprint 015: English repository prose + +Starting revision: `594519f`. Status: complete. +Mapping: cross-cutting repository documentation; no F0–F5 phase advancement. + +## Plan and acceptance + +1. Inventory repository text and translate existing non-English prose, including + historical research, plans, application contracts, evaluation, and sprint records. +2. Preserve requirements, formulas, thresholds, raw artifacts, historical outcomes, + source identities, and links. Add the English requirement to canonical guidance. +3. Check remaining text, Markdown links/fences, tests, lint, and documentation; + record the result and commit independently from evaluation data preparation. + +Smallest counterexample: an existing Chinese heading in a historical plan violates +this requirement even if every new file is English. Mathematical symbols, source +identifiers, and literal data values are not prose to translate. + +## Results and verification + +All existing Chinese prose in repository files has been translated. AGENTS.md and +the sprint execution rules now require English for future prose, comments, and +instructions. Historical recommendations remain historical; the current foundation +mission, full R1–R9/C1–C7/A1–A13 scope, and evaluation-first order are unchanged. + +Validation: + +- Scanned every Git-tracked and nonignored untracked UTF-8 text file for CJK + ideographs; none remain. Reviewed other non-ASCII letters: mathematical Greek, + transpose/subscript symbols, and real-number notation remain intentionally. +- Compared source URLs and long hexadecimal identities against the previous + revision. Three retired implementation link targets now use their verified + historical Git revision; no evidence source was removed. +- Rendered Markdown links with fenced-code/table handling and checked local file + targets in changed documents and incoming anchors to changed documents: pass. +- Code fences are balanced; `git diff --check` passes. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest tests/ -n 0 -q`: + 396 passed, no skips. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync ruff check src/openboost benchmarks/v1 tests/v1 tests/conftest.py`: + pass. +- `UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync mkdocs build --strict`: + pass. The separate link audit covers planning/sprint files outside MkDocs navigation. + +No raw benchmark artifact or production implementation changed in this slice. +The Adult data preparation is the separate preceding commit `594519f`. + +## Reflection + +Observation: requirements and historical evidence were split across languages. +Evidence: the inventory found non-English prose in active specifications and old +strategy/execution records, not in runtime code or frozen data. +Decision: translate the entire prose surface while preserving the distinction +between superseded recommendations and active v1 requirements. +Next: continue B02/F0.3 data, baseline capability, budget, runner, and quality-judge +preparation. English documentation does not establish implementation, GPU performance, +model quality, or external adoption. F0.3 and F1–F5 remain incomplete. + +## Commits + +- This slice: `docs: use English throughout repository prose`. diff --git a/v1-sprints/016-f0-3-completion.md b/v1-sprints/016-f0-3-completion.md new file mode 100644 index 0000000..b3c9be2 --- /dev/null +++ b/v1-sprints/016-f0-3-completion.md @@ -0,0 +1,268 @@ +# Sprint 016: Complete evaluation preparation + +Starting revision: `dc74401`. Status: in progress. Mapping: B02/F0.3. + +## Plan and acceptance + +1. Freeze remaining real data and task-specific five-split semantics, source/license + evidence, preprocessing, and source/array/row hashes for every A1–A13 application. +2. Verify installed comparator capabilities on CPU then real CUDA; freeze versions, + environments, 16 configurations per method, and resource limits before quality runs. +3. Implement process execution and independent quality/selection judging with + adversarial checks; freeze author evaluation inputs without contaminating held-outs. +4. Audit every F0.3 exit requirement, record unresolved evidence explicitly, and + commit independently verified slices. Do not mark F0 complete based on test totals. + +First counterexamples: rows from one subject/recipe must never cross data partitions; +invalid or missing comparator output must not yield a passing quality gate. + +## Reflection and evidence + +Pending. Public production components remain F1 work. External source failures, +package availability, and held-out independence must be resolved without silently +changing the required application scope or evaluation thresholds. + +### Data slice + +Covertype, Parkinsons, Concrete, Veteran, and insurance inputs/splits are frozen. +Nine focused tests passed, including source corruption and hand-worked joins. +Every actual source was parsed and replayed; no model quality was inspected. +The join excludes 195 orphan claims and identifies 9,116 positive-count/no-payment +policies. Grouped partitions preserve subjects, recipes, and policies. This +validates target/data semantics without creating any new production implementation. +Remaining source blockers: MSLR retrieval/agreement and Housing/Veteran license +confirmation. Required applications and all original gates remain intact. + +### Evaluation machinery slice + +Actual train-only encoding hashes now cover available data, with separate A8/A9 +training populations and A10 censoring support. Independent metrics and five-fold +comparisons retain every target/quantile; altered prediction row order fails even +when file hashes are recomputed. Worker zero exit without artifacts, nonzero exit, +and timeout fail. Seventeen focused checks and 422 total tests passed. + +Reflection: a real CUDA comparator abort showed why the evaluation process boundary +matters. Artifact/metric checks and baseline probes remain distinct from complete +task execution. Full validation-selection receipts, expected recipe/device matrix, +auxiliary task metrics and held-out cohort integration are still unfinished; +partial modules must not create an F0.3 completion label. + +### Comparator capability slice + +A real T4 isolated-process matrix completed: 29 CPU / 28 CUDA passing built-in +cells, four/five explicit unsupported cells. Nonunit weights, native mixed data, +external offset persistence and secondary algorithms have separate CPU artifacts. +See the [evidence index](../benchmarks/v1/evidence/README.md). + +Reflection: the standard LightGBM wheel is not the CUDA comparator. Source builds +and process isolation were necessary. Keep the failed build, native abort and +original tolerance mismatch; a later passing preflight does not erase failures +or prove mixed-library context safety. No end-to-end quality/speed claim follows. + +Py-Boost 0.5.2 also passed weighted scalar/vector GPU fitting and JSON reload. +Its initial zero-verbosity callback failure remains in the evidence directory. +These checks do not cover all Py-Boost E5 arms or real task metrics. + +### Validation worker slice + +The fixed-round numeric worker passed 30 CPU task/library fit/reload cells and +exposure doubling in the three count adapters. Nine adversarial checks reject +invalid exposure, censoring, weights/IDs and unsupported options/test arrays. +The complete suite is 431 passing tests. This worker explicitly rejects the +search design's early-stopping option; search execution is not ready to freeze. + +### Current exit audit + +F0.3 remains **in progress**, and F1 has not started. Remaining acceptance work: + +1. Resolve A4 ranking source/agreement and Housing/Veteran license evidence. +2. Complete query-aware, composed A9, structural A12 and A13 adapters; implement + early stopping and the validation-selection/test-release path, with corruption + and leakage counterexamples. Bind the independently expected full matrix. +3. Complete auxiliary application reports, including classification and IPCW + survival metrics, plus paired uncertainty reports and the E-gate reconstruction. +4. Freeze exact native builds, agent cohort/settings/accounting and E4 workloads. +5. Freeze H1/H2 outside the foundation designer's context. A separate evaluator + requires the user's delegation authorization; the pending request is unanswered. +6. Re-run the complete F0.3 audit and close this sprint only with all requirements + satisfied. Neither comparator smoke totals nor reference tests waive a gap. + +### Next slice: validation selection and test release + +1. Add adversarial fixtures for missing/failed trials, altered configurations, + invented validation scores, changed model bytes and overlapping row IDs. +2. Independently recompute every trial's validation metrics against a separately + pinned protocol; select across all declared methods and seal an immutable receipt. +3. Require the pinned receipt and unchanged artifacts before reading test features. + Verify the boundary with a synthetic complete 16-trial search and document the + distinction between this API ordering and operating-system access isolation. + +Selection slice result: 12 adversarial tests passed, and a complete synthetic +16-trial XGBoost subprocess search passed audit/seal/release followed by new-process +selected-model inference. Scores are recomputed independently; the receipt binds +all trial outputs. Test features are not read by the selector. A forged winner, +changed model, omitted trial or row overlap fails. + +Reflection: content hashes alone do not prove that a worker never accessed test +files. The API enforces sequence and consistency; restricted mounts/execution +provenance still need integration. The full required real-data matrix, early +stopping, auxiliary metrics and independent held-outs remain open. Do not turn +this synthetic orchestration success into a formal E3 or F0.3 pass. + +### Next slice: baseline early stopping + +1. Require explicit validation targets/weights (and censoring for A10) when early + stopping is enabled; reject malformed or unused validation fields. +2. Use pinned native stopping implementations and retain the selected tree count + in saved prediction state. Record each method's stopping metric and history; + independent cross-method selection continues to use prediction-space metrics. +3. Exercise scalar, vector, quantile, offset and distribution paths in the locked + CPU environment, with a counterexample whose best iteration precedes the last. + Verify replay of the selected iteration, including a new-process check. + +Early-stopping result: the 30 supported CPU task/library cells passed weighted +validation stopping and replay. Selected rounds agree with recorded native metric +minima. Deliberate overfitting cases select round 1 and reproduce after loading in +fresh processes across XGBoost, LightGBM, CatBoost and NGBoost. Patience-three smoke +settings do not change the preregistered patience of 50. + +Reflection: native default prediction is not universally best-iteration prediction. +Preserve limits explicitly for XGBoost/NGBoost and retain each LightGBM fit's limit. +Record native stopping metrics separately from independent method selection. +CPU evidence does not establish CUDA stopping or complete F0.3 acceptance. + +### Remaining adapter work + +Implement A4 query-aware workers first: contiguous groups, disjoint query IDs, +explicit per-query weights, native ranking objectives/stopping and saved scores. +Reject row weights and fragmented groups before entering a comparator. Then add +parametric/composed A9/A12 controls and auxiliary metrics against hand-worked +oracles. Source/license and held-out requirements remain separate exit conditions. + +Ranking adapter result: all three CPU rankers passed weighted query-aware fitting, +named-NDCG stopping and reload. Five group/weight/overlap counterexamples passed. +The original smoke's metric-order assumption failed on CatBoost and is retained; +selecting a metric by position is unsafe when a library adds a second history. +Real ranking data/agreement remains unresolved; A4 quality is not passed. + +Parametric adapter result: A7/A8/A9 GLMs, matched paid-count × severity, and the +A12 global formula now execute through strict validation workers. Five subprocess +checks matched independent hand-calculated outputs. Six input/math tests reject +invalid joins/structure. The full suite is 460 passing tests. Composition penalty +pairs are declared before real quality evaluation. + +Reflection: paying claims and recorded claims are distinct target populations. +Validate exact paid-record reconstruction before fitting either component; the +same output units must survive model composition and reload. These controls do +not implement FormulaBoost's coupled updates or finish full task/matrix binding. + +Auxiliary slice: weighted classification diagnostics, Normal PIT, supported-grid +IPCW Brier, separately named Harrell C, training-age support errors and descriptive +paired intervals are implemented. The quality report retains complete fold metrics +and explicitly lists missing A10/A12 auxiliary inputs. Rehashed invalid support +still fails. Thirteen focused metric/artifact tests pass. + +Reflection after these three implementation slices: component-level adapters and +judges now cover more task semantics, but a complete pinned real-data execution +matrix, outer coupled controls, A13 scheduling, native-build/agent provenance and +held-out independence still require work. Green synthetic checks cannot close F0.3. + +### A6 target-space correction + +Plan: standardize all comparator A6 targets from training rows only, preserve the +constant-column rule and zero standardized base, invert predictions after fit and +reload, then exercise mismatched target scales and fresh-process replay. Native +stopping metrics operate in standardized target space; final metrics use original +units and the frozen training scale. This corrects a task-contract gap before +real-data trials. + +Result: 30 fixed and 30 stopping CPU cells pass, including fresh-process A6 +replay across three comparators. All 474 tests, Ruff and strict MkDocs pass. + +Source review: the exact Housing archive matches Figshare version 2's MD5 and +CC BY 4.0 declaration. A dated overlay preserves the original freeze and records +uploader attribution. MSLR agreement retrieval still returned HTTP 401; Veteran +original-source license remains unresolved. + +Held-out preparation: user authorized a separate evaluation agent, which sealed +H1/H2 cards/verifiers and reported passing internal/adversarial validation. Only +hashes and status reached the foundation designer. The opaque package is retained +in ignored local storage and its hash is in the public held-out manifest. This +is separate-context authorship, not OS isolation or a completed E5 cohort. + +Reflection: task-space normalization is part of comparator fairness, not cosmetic +preprocessing. The constant-target counterexample found a real native-library +contract difference. Held-out custody now exists, but does not remove the need +for restricted candidate execution and frozen cohort accounting. + +### Real-data worker binding + +1. Bind Housing A1/A11, Parkinsons A6 and Concrete A12 to existing verified + sources and all five frozen preprocessing partitions. Reject changed rows, + encoders, targets and subject/recipe overlap before export. +2. Export validation worker inputs separately from test features and test truth; + preserve original target units and keep A12 age explicit in baseline features. +3. Run real-data CPU plumbing checks and record hashes/reload results. These + checks do not replace the 16-trial search, quality matrix or OS isolation. + +First counterexample: a valid-looking reordered partition or modified training +encoder must fail even if array shapes remain identical. + +Binding result: all 20 exported fold packets reproduced their frozen preprocessing. +Twenty four-round real validation fits passed in fresh CPU processes, with A6 +saved scale matching the independent freeze. All 481 tests, Ruff and strict MkDocs +pass. Scope is A1/A6/A11/A12 plumbing; remaining applications and full searches +are still open. Separate files do not establish OS-enforced test isolation. + +### Classification and quantile binding + +1. Add Adult A2, Covertype A3 and Bike A5 using their frozen reader, source hashes + and encoders. Preserve physical/source row IDs and train-only category vocabularies. +2. Validate Bike's expanding chronological prefix rather than requiring every + fold to include future rows; reject gaps, overlap and dates crossing boundaries. +3. Exercise all five new real folds in bounded CPU workers and retain failures, + probability/quantile schemas and raw provenance. Keep earlier bindings covered. + +First counterexamples: future rows must not enter an early Bike origin; unseen +Adult validation categories must not expand the training vocabulary. + +Result: all 35 real-data validation fits passed (seven application paths by five +folds), including prior bindings. All 484 tests, Ruff and strict MkDocs passed. +Raw evidence is `benchmarks/v1/evidence/real-classification-quantile-binding-cpu.json`. + +Reflection: source row identities and positional preprocessing indices serve +different purposes and must not be conflated. Chronological evaluation also +requires deliberately unused future data; universal full-coverage validation +would silently change the task. No required application was dropped. + +### Counts, paid amounts and survival binding + +1. Bind A7 policy counts, A8 positive paid claims, and A9 eligible-policy paid + totals using the already frozen policy splits and population-specific encoders. +2. Preserve A7 exposure as an offset input; convert A9 totals to annualized targets + and apply exposure weights exactly once. Bind A10 events and frozen training G. +3. Test hand-worked population/weight/censor counterexamples and real five-fold + CPU worker execution. Keep source-license and full quality gates separate. + +First counterexamples: two claims from one policy must not cross folds; A9 must +not receive both exposure weights and an exposure offset or use raw claim counts. + +Reflection after three binding slices: real-data packet coverage now exercises +scalar, categorical, quantile, vector, positive/count and survival contracts. +Population selection, target-space conversion and row identity are independently +checked rather than left implicit in workers. This is evaluation infrastructure; +it does not provide production foundation components or certify complete recipe +coverage. Next binding gaps are ranking and train-many, with parametric/composed +and coupled controls still requiring their own execution paths. + +Result: all 20 new real-data CPU validation fits passed across A7/A8/A9/A10 +and five folds. All 488 v1 tests, Ruff and strict MkDocs passed. Raw evidence: +`benchmarks/v1/evidence/real-positive-survival-binding-cpu.json`. No quality gate +or source-license gate was promoted by these plumbing checks. + +## Sequencing audit + +[Sprint 017](017-f0-sequencing-audit.md) audits remaining F0.3 obligations against +later evaluation results. F0.3 remains incomplete under the current gate. A bounded +B03–B06 overlap is proposed, not adopted; all v1 scope and thresholds are retained. +The audit also identifies missing A6 score/scale binding and global gate aggregation. diff --git a/v1-sprints/017-f0-sequencing-audit.md b/v1-sprints/017-f0-sequencing-audit.md new file mode 100644 index 0000000..bad9edf --- /dev/null +++ b/v1-sprints/017-f0-sequencing-audit.md @@ -0,0 +1,124 @@ +# Sprint 017: F0 prerequisite and sequencing audit + +Audit base: `8565f77` (clean worktree). Status: audit complete; sequencing amendment approved by the user on 2026-09-06. +The original proposal below is retained as the audit record; active adoption is +in the main plan and Sprint 018. No production implementation or remote publication in this slice. + +## Question and verdict + +Must all remaining evaluation work finish before building the foundation? + +No final E0–E7 result is required before F1. However, the current written F0.3 +requires more than task cards and mathematical oracles: it explicitly requires +protocol/matrix/environment/agent freezes, adapters, runner and judges. F0 exit +also says missing data or judges prevent exit and F0.1–F0.3 must precede F1. +Consequently **F0.3 is not complete and F1 is not authorized by the current phase +gate**. The earlier suggestion that we could simply treat all comparator and +cohort work as later work understated that gate. Starting B03 early requires an +explicit sequencing amendment; this audit does not silently enact one. + +Engineering assessment: the missing global evaluation preparation does not all +block construction of Numeric Problem, RunContext, AcceptedState and minimal +artifacts. Requiring every evaluation adapter before testing these abstractions +has delayed the product hypothesis: composable components should make correct +algorithm changes easier. Three data-binding slices improved fairness, but did +not test whether the proposed foundation works. + +## Evidence inspected + +- [Active plan F0–F5](../planning/agent-boosting-foundation-plan.md): F0.3 freeze/ + implementation gate; F1 CPU correctness, F2 agent trials, F3 device/cost, F4 + real quality/adoption and F5 delivery. +- [B02–B14 construction order](../planning/foundation-construction-design.md): + B03 starts public implementation; real evaluation follows stable recipes. +- [E0–E7 protocol](../planning/openboost-v1-evaluation.md) and + [A1–A13/D1–D5 task contracts](../planning/foundation-tasks.md). +- [F0.2 acceptance ledger](f0-2-acceptance-ledger.md), reference implementations, + actual worker/exporter/selection/quality/integrity call paths and tests. +- Frozen dataset records, search design, CPU/CUDA locks, comparator artifacts and + safe held-out manifest. Held-out task/verifier contents were not inspected. +- `src/openboost/` contains only `__init__.py` and `py.typed`; public v1 training + and prediction components have not been implemented. + +## Findings and phase assignment + +“Before F1 as written” reports the existing plan, not a claim that the dependency +is technically necessary for B03. Later deadlines below describe execution of +results; protocol definitions must precede those results. + +| Item | Actual state | Before F1 as written? | Necessary deadline / recommendation | +|---|---|---|---| +| A1–A13/R1–R9/C1–C7 contracts and independent math/state references | F0.1/F0.2 delivered; all families mapped, not production support | Yes; satisfied at specification/reference level | Use immediately as B03–B11 conformance oracles | +| Real data and split identities | Frozen inputs for A1–A3/A5–A12; A13 explicitly reuses Covertype/Housing | Yes; incomplete for A4 | Resolve A4 source/agreement and Veteran license record; do not invent a separate missing A13 dataset | +| CPU comparator capability and budgets | Pinned libraries, CPU probes, 16-config families, 1800-second/8192-MiB/two-thread budgets exist | Yes; substantial partial evidence | Stop describing all baseline/budget preparation as absent; complete task binding before its scored search | +| CUDA comparator capability | Real T4 preflight exists; LightGBM native build/isolation gaps recorded | Yes for environment freeze | Full native build closure is necessary before formal F3/E4 comparison, not Numeric Problem construction | +| Real numeric worker wiring | Eleven applications, five folds each exercised across committed artifacts; four-round validation plumbing | Adapter preparation belongs to F0.3 | Preserve evidence; no need to rerun every preceding cell merely to begin a new CPU component | +| A4 ranking wiring | Synthetic query-aware workers tested; real dataset unresolved | Yes; incomplete | Source first, then real query binding; retain ranking in B09 and full v1 scope | +| A13 scheduler/comparison wiring | Reused datasets and independent M=1/8/32 state oracles exist; real workload/scheduler/cost execution incomplete | Freeze/adapters yes; final execution no | Freeze workload membership before timed evaluation; build sequential OpenBoost semantics at B06, fused/device work at B13 | +| Composed A9, outer/coupled A11/A12 and other comparator paths | Parametric workers/diagnostics exist; complete real integrations do not | Adapter preparation is part of current F0.3 | Match controls before each scored quality/author comparison; cannot substitute ordinary GBDT for these algorithms | +| Global expected matrix | Local smoke matrices and integrity schema exist; no complete frozen recipe/task/device/fold comparison matrix found | Yes; incomplete | A real remaining protocol deliverable, not another batch of four-round fits | +| Independent gate evaluation | `judge.py` returns integrity only and empty gate results; `quality_report.py` always sets E3 false; selection seal/audit works for its declared packet | Yes for judge implementation; incomplete | Define fail-closed gate aggregation and required evidence; execute CPU conformance during F1, other gates in their assigned phases | +| H1/H2 preparation | Separate evaluator authored sealed package; hashes/status committed; local custody and independence limitations explicit | Yes; partially satisfied | Do not call the cards missing. Keep contents out of design; finish restricted execution and comparator/cohort preparation before formal F2 | +| Agent cohort and accounting | Numeric attempt limits fixed; model/reasoning/tools/docs/cache/accounting cohort not bound | Explicitly yes; incomplete | Cohort must freeze before scored F2.2; it is not an input to B03 math/state semantics | +| Full 16-trial quality searches and test results | Not completed; four-round smoke is not selection evidence | Results: no | E3/F4 after runnable recipes; comparator searches may run earlier once protocol/data/environment are frozen | +| CPU E0/E1/E2 success | References exist, public conformance does not | No; needs production code | F1 exit, not F0 entry requirement | +| GPU parity, end-to-end cost and fused train-many wins | No new OpenBoost implementation/results | No | F3/E4; no device or speed claim before passing | +| Agent advantage, package delivery, independent repeated adoption | No formal results | No | F2/E5, F5/E6, F4/E7 respectively; outreach still requires authorization | + +## Concrete gaps found in the implementation audit + +1. **Global freeze/aggregation is the missing integration deliverable.** Passing + local worker cells cannot establish complete R/C/A coverage. The integrity + judge validates a producer-declared matrix; it does not determine full recipe + coverage or evaluate E0–E7. Quality reporting deliberately refuses E3 closure. +2. **A6 final scoring is not fully wired to the frozen scale.** Workers now save + train-only normalization correctly. `quality.py` reports per-target RMSE; + `quality_report.py` has no standardized-average output. `selection.py` permits + positive selection weights but does not verify them against the training-scale + artifact. Its comment is not enforcement. Fix this before a scored A6 search; + it does not block constructing B03 state records. +3. **Resource and test-access enforcement differ from declarations.** Search + budgets are present, while the local runner enforces time/threads, not its RAM + cap or filesystem isolation. Separate test files and a hash seal do not prove + that candidate processes could not access test labels. Formal runs need an + execution boundary and provenance, not an invented pass from local smoke. +4. **Some status text is stale.** Historical sections still say held-outs or + train-fitted encoders are missing, despite later artifacts. Use this ledger and + artifact scope rather than treating every old “pending” sentence as new work. + Do not rewrite old failure artifacts or turn later success into retroactive passes. + +## Recommended sequencing amendment (proposal only) + +Allow **B03–B06 CPU architecture construction** to overlap the remaining B02 +preparation. Keep F0.3 marked incomplete until its explicit ledger closes. Do not +allow CPU interface freeze, formal agent comparisons, quality/speed claims or +v1 completion through this overlap. All R1–R9/C1–C7/A1–A13 scope stays required. + +The bounded order would be: + +1. Record adoption of this sequencing amendment in the active plan and define + the first CPU conformance mapping. No threshold changes or source substitutions. +2. Build B03: Numeric Problem, RunContext, AcceptedState and minimal artifact. + Acceptance: distinct run IDs, read-only input ownership, weight/offset applied + once, rejection leaves accepted/best/RNG state unchanged, corrupt-state failure + and fresh-process roundtrip. Use existing independent state/persistence oracles. +3. B04/B05: one shared split/route/leaf pipeline, then squared-error and Normal + recipes. Require two-round intermediate agreement, accepted/rejected updates, + predictions and persistence, with a replaceable operation through public APIs. +4. B06 immediately probes Formula and heterogeneous sequential train-many state. + Do not stabilize interfaces around only scalar GBDT. Then complete B07–B11. +5. Finish the global matrix, source gaps and independent gate/selection binding + alongside the appropriate implementation slices; freeze each formal cohort + before measurement. Full quality and GPU/adoption work stays in F2–F5. + +If the amendment is not adopted, follow the existing gate: finish B02's source, +full matrix, judge, comparator and cohort/environment preparation before B03. +That is the literal plan, but not the recommended engineering critical path. + +## Verification and limits + +Read-only source/artifact audit plus documentation checks; no new model fits or +production changes. Prior committed evidence records 488 passing v1 tests and +55 distinct application/fold numeric plumbing cells across 11 applications; +these are not new audit test runs or complete quality gates. No remote fetch, +push, external publication, held-out inspection or threshold change occurred. diff --git a/v1-sprints/018-b03-cpu-state.md b/v1-sprints/018-b03-cpu-state.md new file mode 100644 index 0000000..b5e2157 --- /dev/null +++ b/v1-sprints/018-b03-cpu-state.md @@ -0,0 +1,43 @@ +# Sprint 018: B03 CPU ownership, state and minimal artifacts + +Starting revision: `de3015d`. Status: initial B03 slice complete. User approved B03–B06 overlap +with unfinished F0.3; all v1 requirements and thresholds remain unchanged. + +## Plan and acceptance + +1. Public numeric prepared data and Problem: own immutable arrays, content/order/ + feature identity, aligned target/weight/offset roles; CPU only, reject unsupported + devices/fields. Numeric bin fitting remains B04. +2. Explicit RunContext and accepted/proposed state: deterministic keyed randomness, + separate train/validation raw caches, offsets applied at output only, atomic + accept/reject, stale/cross-run rejection and immutable best snapshots. +3. Minimal constant-term inference artifact to exercise B03 without a tree grower: + explicit vector base/coefficients, shape/version/corruption checks and fresh-process + prediction roundtrip. This is not a trained boosting recipe. +4. Verify independent hand/reference cases, full CPU regression, lint, docs and + package build; update public capability claims and commit verified slices. + +First counterexamples: mutating caller arrays cannot alter owned problem/model +state; two rejected trials cannot change raw predictions, best state or keyed RNG; +repeated updates cannot accumulate an input offset a second time. + +F0.3 remains open. B04/B05 implement trees and the first complete recipes. B06 +must test Formula and heterogeneous run state before interface stabilization. + +## Result and reflection + +Initial B03 slice delivered: public NumericData/Problem, RunContext, immutable +accepted/proposed state and constant-term inference. All 509 tests pass, including +21 public cases. Ruff, strict docs, runnable documentation, sdist/wheel build and +isolated wheel inference checks pass. See the +[learning record](../learnings/2026-09-06-v1-b03-cpu-state.md). + +A content-bound parent is necessary: version alone cannot distinguish divergent +accepted histories. Offsets must stay outside cached raw to avoid compounding +exposure across commits. These are shared state boundaries, not task-name branches. + +This is an initial CPU architecture milestone, not all F1.1 semantics or CPU v1 +acceptance. No tree training or GPU implementation exists. Next: B04 numeric +binning/statistics/candidates/routing/leaves, then B05 squared/Normal compositions. +B06 must exercise Formula and heterogeneous runs before interface stabilization. +F0.3 stays open under the approved overlap, with every requirement retained. diff --git a/v1-sprints/019-b04-numeric-operations.md b/v1-sprints/019-b04-numeric-operations.md new file mode 100644 index 0000000..40015e5 --- /dev/null +++ b/v1-sprints/019-b04-numeric-operations.md @@ -0,0 +1,37 @@ +# Sprint 019: B04 numeric preparation and composable operations + +Starting revision: f419b32. Status: initial operations slice complete. Approved B03–B06 overlap applies; +F0.3 remains open and every v1 application remains required. + +## Plan and acceptance + +1. Fit train-only numeric quantile cuts and produce owned feature-major int32 + codes with separate missing masks. Verify against the independent order-statistic + oracle, including minimum cuts, duplicates, all-missing and unseen ranges. +2. Public row fields and exactly-once Newton weighting; independent information + fields remain unweighted. Aggregate selected rows into per-feature histograms. +3. Public candidate enumeration, scoring, feasibility, choice, routing and scalar + leaf solving. Compare histogram-prefix results with exhaustive original-row + enumeration, including missing routes and custom cohort constraints. +4. Verify tests/docs and commit. Depthwise tree assembly, tree persistence and + integration into runtime are the next B04 slice; this slice claims operations only. + +First counterexamples: weights applied twice must fail; a high-gain split that +violates independent cohort mass must be replaceable through public feasibility. + +## Result and reflection + +Initial operations slice complete: 527 tests pass, including 18 public-operation +cases. Histogram-derived feasible candidates/gains/routing/leaves agree with the +independent original-row oracle. Both public documentation examples execute from +an isolated installed wheel; lint, strict docs and build pass. See the +[learning record](../learnings/2026-09-06-v1-b04-numeric-ops.md). + +A coarser binning can erase a task's distinguishing candidate. Reference fixtures +must preserve the intended candidate set rather than silently turning an algorithm +change into a preprocessing comparison. Weight provenance and row/transformer +identities remain explicit through aggregation and partition. + +Next B04 slice: assemble a depthwise tree through these public operations and +verify inference/persistence. Full B04, F1 and F0.3 remain incomplete. There is no +GPU or end-to-end boosting result from this slice. diff --git a/v1-sprints/020-b04-depthwise-tree.md b/v1-sprints/020-b04-depthwise-tree.md new file mode 100644 index 0000000..5e5186b --- /dev/null +++ b/v1-sprints/020-b04-depthwise-tree.md @@ -0,0 +1,36 @@ +# Sprint 020: B04 depthwise numeric tree + +Starting revision: b77dbc5. Status: complete for this slice. The approved B03–B06 overlap +applies; F0.3 remains open. + +## Plan and acceptance + +1. Assemble depthwise growth through public histogram, candidate, feasibility, + scoring, routing and leaf callbacks. Compare topology and predictions against + the independent exhaustive oracle, including limited leaf budgets. +2. Validate explicit child-index topology and persist the numeric transformer, + missing routes and scalar leaves. Reject corrupt graphs and round-trip in a + fresh process with unseen numeric values and missing values. +3. Run focused and CPU regression tests, lint and documentation checks; record + limitations and commit the verified slice locally. + +The first acceptance check is an independent depthwise topology/prediction comparison. +Callback checks must demonstrate a changed algorithm, not merely a callable API. +This slice does not integrate tree terms into accepted run state or provide a +boosting loop, vector leaves, categories or CUDA. + +## Result and reflection + +The grower and inference artifact are implemented. All 553 CPU tests pass, +including 26 tree cases. Independent exhaustive growth agrees on topology, leaves +and predictions; callbacks demonstrably alter the algorithm. Strict docs, lint, +build and all three installed-wheel examples pass. See the +[learning record](../learnings/2026-09-06-v1-b04-depthwise-tree.md). + +Observation: composing numeric operations into a learner required no task-name +branches or legacy trainer. Layer budgets did require an explicit policy and +cached callback gains, which the independent reference made reviewable. This +supports the scalar tree boundary only; it does not yet validate distributional +or structured learners. Next: integrate tree terms and squared/Normal recipes, +then probe Formula and heterogeneous runs before stabilizing the foundation. +F0.3, full F1 and later evaluation gates remain incomplete. Nothing was pushed. diff --git a/v1-sprints/021-b05-squared-recipe.md b/v1-sprints/021-b05-squared-recipe.md new file mode 100644 index 0000000..ca9cd9a --- /dev/null +++ b/v1-sprints/021-b05-squared-recipe.md @@ -0,0 +1,38 @@ +# Sprint 021: B05 tree terms and squared boosting + +Starting revision: 85e9333. Status: complete for this slice. Approved B03–B06 overlap applies; +F0.3 remains open and all required use cases remain in scope. + +## Plan and acceptance + +1. Replace the constant-only model with a versioned ensemble of constant and + mapped scalar-tree terms; retain immutable transaction and best-state semantics. +2. Compose a complete scalar squared recipe with weighted derivatives, offsets, + fixed steps and bounded backtracking that reuses the learner. Compare multiple + rounds and intermediate values with the independent exhaustive reference. +3. Verify rejection/foreign proposals, mixed-term persistence, offsets, best + snapshots and fresh-process inference. Run regression/lint/docs/build, reflect, + and commit locally. Normal geometry/recipes remain the next B05 slice. + +Smallest initial failure: importing the complete squared recipe for the two-round +reference comparison. No new benchmark quality or performance claim is intended. + +## Result and reflection + +The first complete CPU squared recipe is implemented with mapped tree terms, +fixed/backtracking steps and inference persistence. All 560 CPU tests pass; +independent three-round intermediates agree, and rejection/offset/best-state +checks pass. Ruff, strict docs, build and all four installed-wheel examples pass. +See the [learning record](../learnings/2026-09-06-v1-b05-squared-recipe.md). + +Observation: scalar boosting now composes the same public operations used by +manual algorithm changes. Atomic term tuples allow multiple mapped updates +without specializing runtime for squared loss. Evidence still only validates +scalar-target geometry: Normal will require separating target width from raw +parameter width, currently coupled in Problem/state. Address that next rather +than duplicating labels or building a separate distributional trainer. The CPU +implementation recomputes predictions during trials; optimize after the geometry +and Formula/heterogeneous probes establish the right boundary. + +Normal remains the next B05 slice; B06 must follow before stabilization. F0.3 and +all required evaluation gates remain open. No scope was dropped and nothing pushed. diff --git a/v1-sprints/022-b05-normal-recipe.md b/v1-sprints/022-b05-normal-recipe.md new file mode 100644 index 0000000..dcd156a --- /dev/null +++ b/v1-sprints/022-b05-normal-recipe.md @@ -0,0 +1,36 @@ +# Sprint 022: B05 Normal geometry and shared raw state + +Starting revision: 6491948. Status: complete for this slice. Approved B03–B06 overlap applies; +F0.3 and all required application evaluations remain open. + +## Plan and acceptance + +1. Separate observed target width from explicit raw parameter width in Problem + and state, preserving existing squared behavior and strict offset alignment. +2. Expose Normal ordinary/Fisher geometry and least-squares direction fitting; + compose joint mean/log-scale updates through existing trees and transactions. +3. Compare geometry, base, multiple updates, NLL and persistence with independent + references. Exercise bounded backtracking, nonfinite trials and full rejection. +4. Run regression/lint/docs/build, record evidence and reflect before local commit. + +The first failing check constructs a scalar-target problem with two raw columns. +This slice implements joint Normal updates; ordered updates and Formula remain +separate required probes. No CUDA or predictive-quality parity claim is intended. + +## Result and reflection + +Joint Normal boosting and independent target/raw widths are implemented. All 572 +CPU tests pass; ordinary and natural three-round intermediates match independent +references. Lint, strict docs, build and five installed-wheel examples pass. +See the [learning record](../learnings/2026-09-06-v1-b05-normal-recipe.md). + +Observation: Normal required changing the shape contract, but reused numeric +preparation, scalar growth, mapped terms and transactions. Its likelihood Fisher +and direction-regression curvature are distinct and explicitly tested. This is +support for exposing the geometry-to-fit boundary; it does not establish the +full-metric Formula boundary. Keep that as the next B06 probe, alongside +heterogeneous runs. Do not freeze the current interfaces or claim all F1 work done. + +The squared and joint Normal construction paths now exist. Ordered Normal, all +other application coverage, real evaluation, agent/adoption evidence and CUDA +remain required. F0.3 is still open. Nothing was pushed. diff --git a/v1-sprints/023-b06-formula-runs.md b/v1-sprints/023-b06-formula-runs.md new file mode 100644 index 0000000..2564490 --- /dev/null +++ b/v1-sprints/023-b06-formula-runs.md @@ -0,0 +1,37 @@ +# Sprint 023: B06 Formula and sequential heterogeneous runs + +Starting revision: fd3dbd9. Status: initial probe complete. Approved B03–B06 overlap applies; +F0.3 and complete application evaluations remain open. + +## Plan and acceptance + +1. Bind owned row-aligned structure separately from features; expose saturation + Formula prediction, Jacobian/GGN geometry and damped full-metric directions. +2. Compose Formula through existing learners/transactions and compare two or more + rounds with independent exhaustive references, including rank deficiency. +3. Add sequential run_many with independent contexts, heterogeneous raw widths, + termination lengths and failures. Test M=1/2/8 and reordered scheduling. +4. Verify inference round trips, regression, lint/docs/build, reflect and commit. + +The first failing test requires Formula's structural role, not an added feature. +No fused execution, GPU speed, full A12 value or finished v1 claim is intended. + +## Result and reflection + +Formula and sequential mixed-run probes are implemented. All 581 CPU tests pass; +three Formula rounds agree with independent geometry/growth, and M=1/2/8 jobs +match independent and reordered execution. Lint/docs/build and six installed-wheel +examples pass. See the [learning record](../learnings/2026-09-06-v1-b06-formula-runs.md). + +Observation: the full-metric structured case needed new geometry/structure, but +reused scalar statistics, growth, mapped terms and transactions. Heterogeneous +execution required no shared mutable model or target padding. These are useful +abstraction probes toward programmable boosting. They do not establish agent +productivity, real predictive quality or efficient execution. Runtime remains +CPU-oriented and scalar-tree payloads still constrain upcoming leaf families. + +This closes the initial B03–B06 construction probe, not F1 or v1 acceptance. +Next is B07 growth policies and categorical preparation, followed by vector and +specialized leaves. Keep ordered updates, stopping policies and every required +application/evaluation case visible. F0.3 remains open; no interfaces are frozen. +Nothing was pushed. diff --git a/v1-sprints/024-b07-growth-policies.md b/v1-sprints/024-b07-growth-policies.md new file mode 100644 index 0000000..2e3d8ea --- /dev/null +++ b/v1-sprints/024-b07-growth-policies.md @@ -0,0 +1,33 @@ +# Sprint 024: B07 numeric growth policies + +Starting revision: f6ad95f. Status: numeric policy slice complete. + +## Plan and acceptance + +1. Add best-first heap scheduling and symmetric common-condition layer selection + using existing histogram/scoring/legality/routing/leaf operations. +2. Compare all three policies with independent exhaustive topology/prediction + references, including weights, missingness, leaf budgets and custom callbacks. +3. Exercise recipe substitution and persistence; verify regressions, lint/docs, + record reflection and commit locally. Categorical support remains next. + +The first failing check imports best_first and symmetric for oracle comparisons. +No library parity, speed or completed B07/F1 claim is intended. + +## Result and reflection + +All three numeric growth policies now use shared public operations and the same +inference artifact. All 603 CPU tests pass; 15 independent policy/budget cases +match topology and predictions, with additional callback, symmetric-layer and +recipe/persistence cases. Lint/docs/build and installed-wheel examples pass. +See the [learning record](../learnings/2026-09-06-v1-b07-growth-policies.md). + +Observation: changing growth order required no objective or run-state change. +Symmetric selection did require its own aggregate decision rather than reuse of +per-node winners. The existing operation boundary supported that distinction. +Categorical conditions will next test whether the numeric representation is too +restrictive. Preserve explicit conditions and transformer identity rather than +encoding categories as ordered numeric thresholds. + +Categorical support remains the next B07 slice. Full B07, F1, F0.3 and evaluation +remain incomplete. No speed or upstream-library parity claim; nothing pushed. diff --git a/v1-sprints/025-b07-categorical.md b/v1-sprints/025-b07-categorical.md new file mode 100644 index 0000000..6be70e8 --- /dev/null +++ b/v1-sprints/025-b07-categorical.md @@ -0,0 +1,36 @@ +# Sprint 025: B07 categorical preparation and inference + +Starting revision: ea13cce. Status: categorical slice complete. + +## Plan and acceptance + +1. Add owned mixed feature input and train-only typed category dictionaries. + Generalize NumericBinning/NumericTree to Binning/Tree without compatibility shims. +2. Use dictionary equality candidates with explicit feature kinds through shared + histograms, routing and all growth policies; unknown tokens follow missing routes. +3. Compare mixed topology and predictions against independent raw-row references; + test corrupt dictionaries/schema, fresh-process persistence and recipe integration. +4. Verify regressions/lint/docs/build, reflect and commit locally. No CUDA or + upstream categorical-feature parity claim is intended. + +The first failing test constructs mixed input with a categorical column. + +## Result and reflection + +Mixed input, train-only dictionaries, equality candidates and inference persistence +are implemented across all three policies. All 615 CPU tests pass; independent +mixed-reference topology/output, missing/unseen routing and fresh-process ensemble +round trips pass. Lint/docs/build and all seven installed-wheel documentation +pages pass. See the [learning record](../learnings/2026-09-06-v1-b07-categorical.md). + +Observation: categorical support changed preparation and condition interpretation, +not growth or transactions. Explicit dictionary metadata preserved the distinction +between numeric ordering and categorical equality. Empty dictionaries exposed a +validation difference between histogram storage capacity and valid split values; +validation now uses actual category count. Keep this distinction for upcoming +payload and class schemas rather than treating padded storage as semantic state. + +Next: B08 classification/class schemas and vector leaves/mappings. Full F1 and +F0.3 remain incomplete; every required objective/application and CUDA evaluation +still needs evidence. No interfaces are frozen, no upstream parity claimed and +nothing pushed. diff --git a/v1-sprints/026-b08-binary.md b/v1-sprints/026-b08-binary.md new file mode 100644 index 0000000..e25c75d --- /dev/null +++ b/v1-sprints/026-b08-binary.md @@ -0,0 +1,34 @@ +# Sprint 026: B08 class schemas and binary logistic recipe + +Starting revision: 082b614. Status: binary slice complete. + +## Plan and acceptance + +1. Fit immutable typed class schemas on training labels and bind encoded labels + explicitly to Problem/model identity; reject unknown validation labels. +2. Compose numerically stable binary logistic geometry with shared tree operations + and transactions, including weights, offsets and fixed/backtracking steps. +3. Persist class order with raw ensembles; verify probabilities/decoded labels, + independent multi-round geometry, fresh-process inference and corrupt schemas. +4. Run regressions/lint/docs/build, reflect and commit. Multiclass/vector leaves + remain the next B08 slice; no full classification parity or CUDA claim. + +First failing check: importing the class schema and fitting typed training labels. + +## Result and reflection + +Binary logistic boosting, class-bound state and probability/label persistence are +implemented. All 621 CPU tests pass. Independent three-round geometry/tree checks, +extreme logits, offsets, schema errors and fresh-process inference pass. Lint/docs/ +build and every example across eight installed-wheel pages pass. See the +[learning record](../learnings/2026-09-06-v1-b08-binary.md). + +Observation: binary loss reused scalar split/leaf operations and transactions, +but label meaning had to become explicit state rather than recipe-local metadata. +The schema now follows best models and serialized probability columns. Multiclass +will next test shared vector topology and separate per-channel bounds; do not +approximate that requirement by declaring several scalar models complete coverage. + +Multiclass and vector leaves remain next in B08. F1, F0.3 and full application/ +quality/agent/CUDA evaluations remain incomplete. No interfaces frozen, no parity +claim and nothing pushed. diff --git a/v1-sprints/027-b08-vector-multiclass.md b/v1-sprints/027-b08-vector-multiclass.md new file mode 100644 index 0000000..cca7b50 --- /dev/null +++ b/v1-sprints/027-b08-vector-multiclass.md @@ -0,0 +1,51 @@ +# Sprint 027: B08 vector leaves and multiclass + +Starting revision: 6e5d6b0. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Generalize shared tree payloads and output maps to L-dimensional vectors; expose + vector Newton statistics/scoring/leaves and separate split versus leaf fields. +2. Add softmax geometry with the named diagonal upper bound and joint vector-tree + multiclass updates, preserving class order in inference artifacts. +3. Compare all growth policies and projected splits/full leaves against independent + mixed/vector references; verify multi-round softmax, rejection and persistence. +4. Run regressions/lint/docs/build, reflect and commit locally. No CUDA, full A6 + workflow or external-library parity claim is intended. + +First failing check imports vector_newton for a two-output shared tree. + +## Results + +Delivered vector Newton fields, summed channel scoring and diagonal leaf solves, +all three growers with separate split/leaf fields, [L,K] mappings, joint multiclass +rounds and softmax inference. Tree-v3 stores matrix payloads; older versions fail. + +- Nine focused tests pass against independent mixed/vector and softmax references. + They cover projected/full statistics, topology and predictions across all three + policies, three rounds, arbitrary output mapping, offsets, rejection and + fresh-process persistence on numeric/category/missing/unseen inputs. +- CPU regression: 630 passed. Ruff, strict MkDocs and offline sdist/wheel build pass. +- All ten current documentation Python examples pass from an isolated installed + wheel using Python 3.12.12 / NumPy 2.3.5 on macOS. +- Wheel SHA256: 19ea788e499b34ef3795ee970b3614fd28181b228a296f4bc6423190d5a2cc54. + This identifies a local validation build, not a published release. + +## Reflection + +Observation: split width and leaf width can differ without changing routing, +growth policies or transaction semantics. Evidence: all three projected fixtures +produce a different tree from full split statistics while matching independently +computed full-dimensional leaves. Decision: keep leaf_fields an explicit public +input, and keep projection selection in algorithm code. Multiclass adds no new +tree trainer or state engine. + +The softmax diagonal is an upper bound, not exact curvature; this distinction is +visible in names, tests and documentation. These fixtures validate mechanics, not +real A3/A6 quality or a general sketching speed advantage. Full A6 workflows remain +open. Next follow B09 with ranking/query isolation and routed quantile/penalized +leaf contracts, then complete remaining application/objective slices and CUDA +under the existing plan. F0.3 and F1–F5 are still incomplete. + +Status: complete for this bounded slice. See +[learning and verification](../learnings/2026-09-06-v1-b08-vector-multiclass.md). diff --git a/v1-sprints/028-b09-ranking.md b/v1-sprints/028-b09-ranking.md new file mode 100644 index 0000000..c69a186 --- /dev/null +++ b/v1-sprints/028-b09-ranking.md @@ -0,0 +1,49 @@ +# Sprint 028: B09 query-local ranking + +Parent: 08e323a. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Expose query-local pairwise logistic and NDCG-weighted lambda geometry with + deterministic row-ID ties, explicit query weights and per-query pair normalization. +2. Compose a CPU ranking recipe with existing scalar trees and atomic state. + Recompute lambda ranks each round; evaluate with query-weighted NDCG. +3. Verify independent geometry, query isolation, multiple rounds, offsets, + rejected unsupported row weights and score persistence. +4. Run regressions/lint/docs/build and record reflection before a local commit. + +This bounded B09 slice does not complete quantile/penalized leaves, real A4 +evaluation, pair sampling or CUDA. Explicit pair weights are deferred and not +accepted by this API. The first failing test imports the missing Ranking objective. + +## Results and reflection + +Delivered Ranking geometry and the fixed-step ranking recipe. Query roles are +bound to Problem identity; non-unit row weights and unknown structure fail. +The recipe recomputes geometry per accepted round and selects best_model by +query-weighted NDCG. Pair dependencies require no new tree or transaction engine. + +Twelve focused tests pass: independent pairwise/lambda gradients and curvature, +three-round tree/prediction parity, logistic finite differences, query isolation, +row-ID tie invariance under permutation, offsets, invalid roles, degenerate +relevance and fresh-process score persistence. CPU regression: 642 passed. +Ruff, strict MkDocs, offline sdist/wheel builds and all eleven installed-wheel +documentation examples pass (macOS, Python 3.12.12, NumPy 2.3.5). +Local wheel SHA256: +2c2809558bfe558cf12b8d7476f381e6e02fdaadd0888184fcb8a586ad1ee8e5. + +Observation: the scalar foundation accepts pair-reduced fields unchanged. +Evidence: multi-round results agree with independent pair loops and tree growth. +Decision: keep query-specific preparation in a public objective module, and +avoid interpreting query weights as row weights. Lambda pair loss uses moving +weights and is not the validation metric. Fixed steps keep that distinction +explicit; future line search needs a separately declared frozen-trial objective. + +This is a CPU correctness slice with quadratic per-query enumeration. Pair weights, +sampling, real A4 folds, scalability and CUDA remain unverified. Quantile and +penalized leaves are the next B09 construction slice and must expose routed +residuals/original weights, not just additive histograms. No application scope +or evaluation gate was removed; F0.3 and F1–F5 remain incomplete. + +Status: complete for this bounded slice. Detailed verification: +[learning record](../learnings/2026-09-06-v1-b09-ranking.md). diff --git a/v1-sprints/029-b09-quantile-leaves.md b/v1-sprints/029-b09-quantile-leaves.md new file mode 100644 index 0000000..318973b --- /dev/null +++ b/v1-sprints/029-b09-quantile-leaves.md @@ -0,0 +1,51 @@ +# Sprint 029: B09 routed quantile and penalized leaves + +Parent: 55f3b91. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add owned residual context and routed leaf views retaining global row IDs, + original weights and current residuals; bind to the split problem identity. +2. Implement weighted quantile and anchored quadratic-penalty pinball leaf solvers, + plus a CPU quantile recipe reusing existing split policies and transactions. +3. Verify against independent quantile/D3 oracles, including second-round effects, + all growth policies, weights, offsets, identity and persistence. +4. Run regression/lint/docs/build checks, reflect and commit locally. + +No real A5 quality, author-effort, GPU or performance claim. First failing check: +the public residual leaf context and quantile recipe are missing. + +## Results and reflection + +Delivered ResidualContext/ResidualView and paired row_leaf/leaf_context inputs on +all three growers. Views retain original global row IDs and weights. Quantile +splits use pinball gradients and unit pseudo-curvature, while leaf values solve +weighted residual quantiles or anchored quadratic-penalty pinball. Initialization, +offsets, fixed/backtracking acceptance and best-model selection compose existing +state operations. + +Thirteen focused tests pass: 80 randomized ordinary/penalized solver comparisons, +three rounds under all three policies with and without penalties, original-weight +and row-ID routing, immutability, foreign context rejection, penalty/mass response, +atomic backtracking rejection and mixed-feature inference round trips. +CPU regression: 655 passed. Ruff, strict MkDocs, offline sdist/wheel and all twelve +installed-wheel documentation examples pass on macOS/Python 3.12.12/NumPy 2.3.5. +Local wheel SHA256: +bc2b146fbf2c86b14ea607ad4113779fabacd11ed68655f8825d8399dbb8b351. + +Observation: additive statistics alone cannot recover weighted residual quantiles. +Evidence: independent three-round trees require routed residual solutions, and +changing original weight mass changes the penalized optimum. Decision: keep the +residual context distinct from weighted additive fields and enforce problem +identity at the grower boundary. This context is a scalar residual implementation, +not yet a universal feature/linear-leaf context. Generalize only against the next +concrete use case. + +Split reg_lambda and leaf penalty are distinct; validation uses prediction pinball, +not a model-wide anchored penalty. No agent-effort or real A5 result follows from +implementing the D3 solver. B09 pair weights/sampling remain deferred; full A6 +workflows and F0.3/F1–F5 gates remain open. Next construction slice is B10 positive +target objectives, starting with Poisson/exposure, then Gamma/Tweedie and AFT. + +Status: complete for this bounded slice. See the +[learning record](../learnings/2026-09-06-v1-b09-quantile-leaves.md). diff --git a/v1-sprints/030-b10-poisson.md b/v1-sprints/030-b10-poisson.md new file mode 100644 index 0000000..50fae63 --- /dev/null +++ b/v1-sprints/030-b10-poisson.md @@ -0,0 +1,47 @@ +# Sprint 030: B10 Poisson counts and exposure + +Parent: dd42967. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add count support validation, explicit positive exposure, Poisson likelihood/ + derivatives and offset-aware rate initialization. +2. Compose a scalar CPU recipe and explicit rate/count inference transform. +3. Verify independent geometry and multiple rounds, exactly-once weights/offsets, + exposure scaling, all-zero counts, invalid support and loaded inference. +4. Run regressions/lint/docs/build, reflect and commit locally. + +A7 mechanics only: Gamma/A8, Tweedie/composition/A9, AFT/A10, real data and CUDA +remain separate required work. First failing check imports missing Poisson. + +## Results and reflection + +Delivered Poisson count likelihood/geometry, exposure validation, offset-aware +constant-rate initialization, fixed/backtracking CPU rounds and explicit +rate/count inference transforms. The count/exposure problem uses the same scalar +Newton fields, growers and state; no new training engine or tree format. + +Ten focused tests pass: independent geometry/intercept and finite differences, +three-round tree composition, zero-count initialization, exposure doubling, +invalid count/exposure/unknown-role rejection, overflow/underflow rejection, +atomic rejection and fresh-process mixed-feature output composition. +CPU regression: 665 passed. Ruff, strict MkDocs, offline sdist/wheel and all +thirteen installed-wheel documentation examples pass (macOS, Python 3.12.12, +NumPy 2.3.5). Local wheel SHA256: +3ea7a840b94395907474a1ef4bc2041c91f9ef78311010dc6469278a9fb24419. + +Observation: exposure belongs in the count likelihood, not in automatic sample +weighting. Evidence: offset-aware base has zero weighted intercept gradient, +original weights match independent multi-round geometry, and fixed raw rates +give doubled counts under doubled exposure. Decision: keep exposure as a +required explicit role and rate/count output as a named external transform. + +The artifact remains a generic raw model: consumers retain the Poisson transform +and supply inference exposure/offsets. This does not yet establish a packaged +application artifact with output-family metadata. All-zero positive-weight counts +use an explicit raw-rate initializer, not a hidden prediction/curvature floor. +Real A7 results and E gates remain open. Gamma/A8 is the next B10 slice, followed +by Tweedie/composition/A9 and AFT/A10; none are substituted by Poisson success. + +Status: complete for this bounded slice. See +[learning record](../learnings/2026-09-06-v1-b10-poisson.md). diff --git a/v1-sprints/031-b10-gamma.md b/v1-sprints/031-b10-gamma.md new file mode 100644 index 0000000..c6e2a25 --- /dev/null +++ b/v1-sprints/031-b10-gamma.md @@ -0,0 +1,46 @@ +# Sprint 031: B10 Gamma positive targets + +Parent: dc03382. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add strictly positive Gamma mean geometry, offset-aware initialization and + explicit positive-mean inference. +2. Compose scalar CPU Gamma rounds with original weights and existing state. +3. Verify independent derivatives, three rounds, claim-level versus weighted + averages, invalid support, offsets and fresh-process mixed inference. +4. Run regression/lint/docs/build, reflect and commit locally. + +A8 CPU mechanics only; no fitted dispersion or calibrated distribution claim. +A9 Tweedie/composition, A10 AFT, real data and CUDA remain required. +First failing check imports the absent Gamma objective. + +## Results and reflection + +Delivered Gamma positive-target geometry, offset-aware intercept, CPU +fixed/backtracking recipe and positive_mean inference. Nine focused tests pass: +independent derivatives/base, three-round tree parity, claim-average/count-weight +equivalence, support/structure rejection, backtracking rejection, output support +and fresh-process mixed-feature persistence. + +CPU regression: 674 passed. Ruff, strict MkDocs, offline sdist/wheel and all +fourteen installed-wheel documentation examples pass on macOS/Python 3.12.12/ +NumPy 2.3.5. Local wheel SHA256: +5301e2f6c554163ddada1e32e3f1ca01d7540b5c339eac7d74cabb8752cd4dda. + +Observation: Gamma adds target/weight semantics without new tree/state code. +Evidence: independent multi-round results and claim-average geometry equivalence. +Decision: expose the mean-only objective and require users to specify their +observation unit and eligibility; do not imply estimated dispersion/calibration. + +Across the last three slices, routed residual leaves required one new explicit +context boundary, while Poisson and Gamma reused the existing scalar path. +Readable recipe loops currently repeat configuration/transaction orchestration; +keep objective distinctions visible and consider a small shared scalar loop only +when its tests preserve these per-family semantics. No refactor or performance +claim is justified merely by line count. + +Next: Tweedie/A9 and frequency-severity composition, then AFT/A10. Real A8, +full A6 workflows, application output metadata and F0.3/F1–F5 remain open. +Status: complete for the bounded slice. See +[learning record](../learnings/2026-09-06-v1-b10-gamma.md). diff --git a/v1-sprints/032-b10-tweedie.md b/v1-sprints/032-b10-tweedie.md new file mode 100644 index 0000000..977e99a --- /dev/null +++ b/v1-sprints/032-b10-tweedie.md @@ -0,0 +1,46 @@ +# Sprint 032: B10 Tweedie nonnegative means + +Parent: 373e16d. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add fixed-power Tweedie geometry and offset-aware mean initialization, + retaining exact zero-target handling and explicit finite-range failures. +2. Compose CPU scalar rounds using original weights and positive_mean output. +3. Verify independent formulas, finite differences, three-round trees across + powers, all-zero initialization, weights/offsets and loaded inference. +4. Run regressions/lint/docs/build, reflect and commit locally. + +This is A9 mean mechanics, not frequency-severity composition, real quality, +dispersion fitting or compound distribution inference. AFT and CUDA remain open. +First failing check imports the missing Tweedie objective. + +## Results and reflection + +Delivered fixed-power Tweedie geometry, offset-aware intercept, explicit +all-zero initializer and scalar fixed/backtracking CPU recipe. Zero targets +avoid evaluating log(0); positive terms fail on underflow/overflow. Inference +reuses positive_mean with caller-owned unit conversion. + +Ten focused tests pass: independent three-round trees/geometry at powers +1.1/1.5/1.9, finite differences including zero targets, annualized weights, +all-zero initialization, invalid support/power/roles, rejected updates and +fresh-process mixed-feature mean composition. CPU regression: 684 passed. +Ruff, strict MkDocs, offline sdist/wheel and all fifteen installed-wheel examples +pass on macOS/Python 3.12.12/NumPy 2.3.5. Local wheel SHA256: +f9c4c0604b6a026d503e71ba3d4d540279f60a1b12fae909ff5914bb79223de2. + +Observation: the same scalar Newton path supports zeros and positive noninteger +targets without a new grower. Evidence: multi-power/multi-round independent +parity and zero-target derivatives. Decision: keep fixed power explicit in +fitting/evaluation, and keep annualized loss units separate from Poisson +count/exposure semantics. No automatic exposure structure is accepted. + +This implements mean mechanics, not normalized compound-distribution inference +or estimated dispersion. The generic model still needs external output/unit +metadata. Next is A9 frequency-severity composition, including persisted +two-model dependencies and aligned policy semantics, then AFT/A10. Real A9, +full A6 and F0.3/F1–F5 remain open. + +Status: complete for the bounded slice. See +[learning record](../learnings/2026-09-06-v1-b10-tweedie.md). diff --git a/v1-sprints/033-b10-frequency-severity.md b/v1-sprints/033-b10-frequency-severity.md new file mode 100644 index 0000000..e5742e0 --- /dev/null +++ b/v1-sprints/033-b10-frequency-severity.md @@ -0,0 +1,51 @@ +# Sprint 033: B10 frequency-severity composition + +Parent: bf7ef2c. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Build aligned Poisson/Gamma problems from declared positive-payment counts, + totals and exposure; reject contradictory aggregates and preserve weight units. +2. Add a two-model inference artifact with fixed rate/severity/product semantics, + aligned policy row IDs, explicit offsets and strict nested persistence. +3. Verify multi-round composition, policy reordering, exposure scaling, mismatched + rows, aggregate errors and fresh-process mixed-feature round trips. +4. Run regression/lint/docs/build, reflect and commit locally. + +The caller remains responsible for eligible-payment joins and dataset partitions. +This is A9 composition mechanics, not real quality or calibrated aggregate loss. +AFT and CUDA remain required. First failing check imports the absent composition. + +## Results and reflection + +Delivered paid_loss_problems for matched paid-count/positive-total policy +aggregates and FrequencySeverity for explicit two-model inference. Business +weights apply to frequency; severity averages receive business weight times +paid count. Policy IDs must align at inference. The versioned bundle embeds +both raw models and fixed output roles; Model.from_record reuses existing +strict validation without temporary files. + +Nine focused tests pass: aggregate roles, three-round component training, +product identities, exposure scaling, policy-order invariance/misalignment, +contradictory aggregates, nested corruption/duplicates and fresh-process +mixed/missing/unseen inference. CPU regression: 693 passed. Ruff, strict MkDocs, +offline sdist/wheel and all sixteen installed-wheel examples pass on macOS/ +Python 3.12.12/NumPy 2.3.5. Local wheel SHA256: +8b6610f3db3297e4e0c71744862cdf2cf1a989263c3233a650bfb99105e04615. + +Observation: a raw scalar model alone does not carry the two-stage output roles. +Evidence: swapping dependencies changes bundle identity, and loading the bundle +restores every named prediction without training objectives. Decision: use a +small explicit composition artifact with embedded models and aligned row IDs. +The declaration does not prove model provenance or eligible-payment joins. + +This completes the bounded A9 composition mechanics, not aggregate-quality +selection or real-data evidence. The helper uses policy-average severity with +shared predictors; claim-level covariates and external join auditing remain +workflow responsibilities. Component best models use their respective validation +objectives, not joint aggregate selection. Next is AFT/A10, including censored +target semantics and persisted scale/output transformations. Full A6 and +F0.3/F1–F5 remain open. + +Status: complete for this bounded slice. See +[learning record](../learnings/2026-09-06-v1-b10-frequency-severity.md). diff --git a/v1-sprints/034-b10-aft.md b/v1-sprints/034-b10-aft.md new file mode 100644 index 0000000..e774f61 --- /dev/null +++ b/v1-sprints/034-b10-aft.md @@ -0,0 +1,53 @@ +# Sprint 034: B10 event/right-censored log-normal AFT + +Parent: b9d5ace. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add an explicit event_right target kind retaining valid upper +infinity while + rejecting unsupported censoring and preserving strict ordinary numeric targets. +2. Implement fixed-scale log-normal geometry with stable normal tails and CPU + rounds; persist scale with raw model and median/mean/survival/quantile outputs. +3. Check independent tail/likelihood and multi-round trees, censoring effects, + weights/offsets, malformed bounds and fresh-process mixed inference. +4. Run regressions/lint/docs/build and record reflection before local commit. + +No real A10 quality, IPCW/calibration, left/interval censoring, variable scale or +CUDA claim. First failing check imports missing LogNormalAFT. + +## Results and reflection + +Delivered explicit event_right bounds with valid upper infinity, fixed-scale +log-normal geometry/CPU rounds and AFTModel with persisted scale and +median/mean/survival/quantile transforms. Ordinary numeric targets remain finite; +target kind participates in problem identity. Initial location is weighted log +lower time minus offset, a finite initializer rather than a censoring-adjusted MLE. + +Twenty-three focused tests pass: independent continued-fraction tail comparisons +through z=1e8, three rounds at sigma 0.5/1/2, finite differences, censoring changes, +invalid bounds/scales, atomic rejection, all-censored initialization, monotone +outputs and fresh-process mixed-feature persistence. CPU regression: 716 passed. +Ruff, strict MkDocs, offline sdist/wheel and all seventeen installed-wheel examples +pass on macOS/Python 3.12.12/NumPy 2.3.5. Local wheel SHA256: +28c6bee585f5029228d475520c464c8fb0945060bafac8a76a32824125612016. + +Observation: censored observations require different target validation, but their +row geometry still fits the shared scalar Newton/tree/state path. Evidence: +event-to-censor changes likelihood and updates while multi-round trees match the +independent oracle. Decision: declare target kind explicitly instead of allowing +infinity in ordinary targets. Tail quadrature and the independent continued +fraction provide structurally different checks. + +Across B10, inference semantics also required explicit composition/scale wrappers. +Their declarations preserve output roles, not training provenance. Mean-only +objectives and fixed-scale AFT do not establish calibrated distributions or +real application quality. Learned scale, left/interval censoring, truncation and +CUDA remain unsupported. + +Next: audit CPU coverage against all R/C/A and B11 prerequisites before declaring +construction complete or moving to GPU. In particular A6 workflows, extension +tasks, complete application artifacts, real comparisons and F0.3/F1–F5 remain +open. B10 recipe mechanics do not close these gates. + +Status: complete for the bounded slice. See +[learning record](../learnings/2026-09-06-v1-b10-aft.md). diff --git a/v1-sprints/035-cpu-coverage-audit.md b/v1-sprints/035-cpu-coverage-audit.md new file mode 100644 index 0000000..c81e7a7 --- /dev/null +++ b/v1-sprints/035-cpu-coverage-audit.md @@ -0,0 +1,121 @@ +# Sprint 035: CPU coverage audit before B11 + +Audited revision: b456bf3. Status: complete as an audit, not a phase exit. + +## Plan + +1. Compare public implementation and exercised tests with R1–R9/C1–C7/A1–A13. +2. Verify concrete gaps by reading call paths and running bounded probes. +3. Order remaining CPU/evaluation work without changing required scope or gates. + +## Verdict + +OpenBoost has a substantial tested CPU foundation and eleven built-in recipes. +It does not yet satisfy complete F1/B11 readiness, E0, E1 or E2. Do not infer phase +completion from sprint count or start GPU expansion as though CPU coverage were +complete. Preserve the existing order and all required applications. + +The 716-test suite includes references and evaluation infrastructure. The public +production subset is 228 tests, all passing in this audit. Neither number is an +application-quality or agent-effort result. + +## Application mapping + +All entries below are bounded CPU mechanics, not real-data acceptance. + +| Application / family | Public implementation and evidence | Remaining acceptance work | +|---|---|---| +| A1 / R1 regression | recipes.squared; test_public_squared.py | Real five-split workflow, selected baseline comparison and artifacts | +| A2 / R1 binary | recipes.binary, ClassSchema; test_public_binary.py | Full real workflow and output/evaluation metadata | +| A3 / R1 multiclass | recipes.multiclass, vector trees; test_public_vector_multiclass.py | Real quality and complete classifier workflow | +| A4 / R3 ranking | ranking.Ranking, recipes.ranking; test_public_ranking.py | Pair-weight/sampling limitations, real query splits, NDCG comparisons; fixed steps only | +| A5 / R2 quantile | recipes.quantile, routed residual leaves; test_public_quantile.py | Temporal real evaluation, installed author extension, declared quantile reporting | +| A6 / R8 multi-output | vector fields/leaves, projected split/full leaf fields and mappings; test_public_vector_multiclass.py | Missing complete multi-output squared objective/recipe with independent and shared trees, multi-round target metrics and output roundtrip | +| A7 / R4 counts | recipes.poisson, explicit exposure; test_public_poisson.py | Real frequency workflow/deviance, output-unit/protocol metadata | +| A8 / R4 positive | recipes.gamma; test_public_gamma.py | Real eligibility/weights, Gamma deviance and comparison | +| A9 / R4 aggregate | recipes.tweedie, FrequencySeverity and paid_loss_problems; test_public_tweedie.py, test_public_composition.py | Raw join audit, real aggregate quality and joint selection, units/provenance | +| A10 / R5 AFT | recipes.aft, explicit event_right target, AFTModel; test_public_aft.py | Real censored NLL/IPCW evaluation, scale protocol and dataset-license closure | +| A11 / R6 Normal | recipes.normal, ordinary/Fisher joint updates; test_public_normal.py | Ordered parameter mutations, complete output/metric workflow and real proper scores | +| A12 / R7 Formula | recipes.formula, full GGN saturation structure; test_public_formula_runs.py | Installed external formula mutation/dependencies, complete real and recovery/misspecification workflow | +| A13 / R9 train-many | runs.run_many, stable IDs/error isolation; test_public_formula_runs.py | Shared prepared binning, validation-driven independent stopping, M=32 and grouped/permuted equivalence, real selection and full-set cost | + +Test paths are under tests/v1; public code is under src/openboost. + +## Capability mapping and concrete findings + +| Capability | Verified boundary | Gap or limit | +|---|---|---| +| C1 data/targets | Owned inputs, categories, class order, weights, offsets, structure, explicit event/right bounds | Binding does not certify entity/query/time split independence; real workflow mapping remains | +| C2 composable trees | Shared histogram/candidate/routing with three policies and extra independent fields | External D2 mutation still needs installed public-only execution evidence | +| C3 leaves/outputs | Scalar/vector, separate split/leaf fields, residual views, quantile/D3 solver | A6 regression composition missing; ResidualContext is scalar-specific | +| C4 runtime | Immutable proposal/accept/reject, best-model state, scoped RNG and sequential failures | No validation-patience stop state, no shared prepared input path, no CUDA/workspace/transfer semantics | +| C5 artifacts | Raw models, typed class mapping, AFT scale and frequency-severity roles | Normal/Formula and mean-family output/evaluation metadata remain external; no training-resume claim | +| C6 evaluation/authoring | Independent references, comparator/data/quality/selection infrastructure, sealed held-out manifest | No current v1 external author-package verification or scored author cohort; full expected matrix/E-gate aggregation incomplete | +| C7 usable workflow | Seventeen installed-wheel documentation examples | Complete real baseline→mutation→verification workflows are missing, particularly A6/A13 | + +### Directly reproduced gaps + +- Calling recipes.squared on a two-column target raises: + "squared recipe requires scalar [N, 1] targets". Shared multiclass trees do + not establish A6 multi-output regression. +- All eleven recipe signatures lack an early_stopping argument. Each recipe + loops over a fixed round budget; best_model retention is not early stopping. +- Each recipe calls Binning.fit(...).transform(...) internally. run_many shares + immutable Problem/data records but does not reuse fitted binning across fits. +- Public run-many tests parameterize M=1/2/8, not M=32. Different round budgets + exercise configuration independence, not validation-triggered stop isolation. +- Normal and Formula build both channel terms from one snapshot and commit + jointly. There is no ordered parameter-update recipe; D4 still needs execution. +- examples/extensions/demo.py imports openboost.experimental, which is retired. + Those packages are historical evidence, not verified v1 extension wheels. +- benchmarks/v1/baseline_worker.py accepts xgboost/lightgbm/catboost/ngboost only. + Existing baseline smokes do not run current OpenBoost or establish its quality. +- No CUDA implementation exists: RunContext rejects devices other than CPU. + +## Ordered execution checklist + +1. **A6 / R8 completion.** Add multi-output squared geometry and explicit + independent-tree/shared-vector recipes. Require two or more rounds with + original weights/offsets, per-target metrics, projected/full split comparisons, + persistence and rejection against independent references. Reject censored + target kinds explicitly even when all bounds are finite. +2. **R9 / C4 prepared inputs and stopping.** Add validated shared preparation, + independent patience/stop state, and M=1/8/32 same-ID independent/reordered/ + regrouped equivalence with a failed run and different validation stop rounds. + No fusion or speed claim. Reuse must verify data/transformer/config identity. +3. **R6/R7 ordered mutation and C5 outputs.** Exercise changed parameter order + and line-search/rejection using public state, with joint versus ordered + reference checks. Complete runnable output dependencies for Normal/Formula + and explicit units/metadata for real application artifacts. +4. **C6 / E2 external author packages.** Implement D1–D5 against current installed + public interfaces, isolated from core edits/private imports. Verify all five + required mutation types, persistence and failures. Exploratory findings can + change interfaces; they are not E5 comparisons. +5. **B11/F0.3 integration.** Connect every A1–A13 to current recipes, frozen + datasets/partitions/config budgets, and independent quality/selection outputs. + Reconcile existing data/baseline preparation instead of repeating smokes. + Resolve A4/A10 source/license issues and complete the expected matrix and + E-gate aggregation. Run only comparisons whose prerequisites are frozen. +6. **Formal author/GPU phases.** Freeze the justified CPU contracts and judges, + run authorized independent held-out evaluation and preregistered author + comparisons, then build required B12 CUDA subsets and B13 batching. Preserve + all real quality, cost and independent-adoption gates. + +This list sequences work; it does not waive other task-card requirements. +Independent F0.3 data/protocol work can progress alongside CPU construction. +Do not inspect sealed H1/H2 contents while redesigning the foundation. + +## Verification and reflection + +- Read implementation, public tests, current docs, main/construction/evaluation + plans, worker dispatch and historical extension imports. +- Ran bounded A6/signature probes on b456bf3. +- UV_CACHE_DIR=/tmp/openboost-research-uv-cache uv run --no-sync pytest + tests/v1/test_public*.py -q --tb=short: 228 passed (macOS, Python 3.12.12). +- Prior full regression at the same production revision: 716 passed, Sprint 034. +- This audit makes no new external release/version, benchmark or quality claims. + +Observation: required families increasingly share real code, but the remaining +gaps concern orchestration, authorability and application evidence as much as +objectives. Decision: prioritize A6 and train-many/stopping over another +unrequested model family or premature interface freeze. Next slice is item 1. diff --git a/v1-sprints/036-a6-multioutput.md b/v1-sprints/036-a6-multioutput.md new file mode 100644 index 0000000..acd655c --- /dev/null +++ b/v1-sprints/036-a6-multioutput.md @@ -0,0 +1,52 @@ +# Sprint 036: A6 multi-output squared CPU recipes + +Parent: 83f6a9a. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add explicit numeric multi-output squared geometry and per-target error metrics. +2. Compose independent scalar trees and shared vector trees, including optional + projected split statistics with full-dimensional leaves, through one transaction. +3. Verify multiple rounds, weights/offsets, all growth policies, rejection, wrong + target kinds and fresh-process persistence against independent tree references. +4. Run regression/lint/docs/build, reflect and commit locally. + +This closes a bounded A6 construction gap, not real subject-split quality evaluation. Shared preparation/stopping/M32 is next. +First failing check imports the missing MultiSquared objective. + +## Results and reflection + +Delivered MultiSquared and multi_squared with independent/shared trees and +optional shared split projection. All outputs commit together; full-dimensional +leaves survive projected splits. K=1 matches ordinary squared boosting. +Added TargetScale and MultiOutputModel for training-only weighted scaling, +constant-target flags and original-unit prediction/offset persistence. + +Fourteen focused tests pass: three rounds across three policies and three modes, +per-output metrics, target permutation, K=1 equivalence, scaling/constant targets, +invalid target/projection rejection, atomic rejection and fresh-process mixed +inference for independent/shared models. CPU regression: 730 passed. Ruff, +strict MkDocs, offline sdist/wheel and all eighteen installed-wheel documentation +examples pass (macOS/Python 3.12.12/NumPy 2.3.5). +Local wheel SHA256: +8934f48ad1a71421ce9f0573a0973a16f6990e13e514fb2d0be2e2b5517d36ed. + +Counterexample: testing variance == 0 misidentified a constant target because +the weighted mean rounded slightly away from its value. Fix: detect identical +values over positive-weight training rows directly, retain that exact mean, +and assign scale one. This is covered by the focused regression. + +Observation: independent and shared regression now use the same objective, +state and three existing growers. Evidence: every intermediate agrees with +independent tree/reference calculations, including projection and output +permutation. Decision: keep raw recipe/scaling separate and persist the inverse +explicitly. Weighted scaling gives a zero weighted base without offsets, up +to roundoff; offsets require a residual-mean correction. + +This closes Sprint 035 item 1's CPU mechanics, including the scaling boundary. +It does not close real A6 subject splits, per-target quality or E gates. Next: +shared prepared inputs and independent validation-driven stopping/M32. Other +audit items and F0.3/F1–F5 remain open. + +Status: complete for the bounded slice. See +[learning record](../learnings/2026-09-06-v1-a6-multioutput.md). diff --git a/v1-sprints/037-shared-preparation.md b/v1-sprints/037-shared-preparation.md new file mode 100644 index 0000000..4f5e620 --- /dev/null +++ b/v1-sprints/037-shared-preparation.md @@ -0,0 +1,46 @@ +# Sprint 037: Shared CPU training preparation + +Parent: 752a2ab. Status: complete for the bounded slice. + +## Plan and acceptance + +1. Add owned preparation binding data identity, bin capacity and fitted codes. +2. Let every built-in recipe and RunSpec accept explicit prepared training data; + reject mismatched data/config and preserve independent target/weight/run state. +3. Verify no repeated bin fitting, M=1/8/32 heterogeneous independent/reordered/ + regrouped equivalence and failure isolation. +4. Run regression/lint/docs/build, reflect and commit locally. + +Preparation covers training binning/codes, not inference caching or fused +execution. Validation-driven independent stopping remains the next slice. +First failing check imports missing PreparedData. + +## Results and reflection + +PreparedData binds immutable training features, bin capacity, fitted transformer +and codes. All twelve built-in recipes resolve this explicit input without a +refit; RunSpec carries it separately from scalar options. A mismatch fails the +affected run rather than rebuilding or contaminating another run. + +Five focused cases pass, including M=1/8/32 heterogeneous squared/Normal/Poisson/ +multi-output independent, reversed and regrouped equivalence. Tests prohibit +Binning.fit after preparation, check distinct raw caches and retain failed runs. +CPU regression: 735 passed. Ruff, strict MkDocs, offline sdist/wheel and all +nineteen installed-wheel examples pass (macOS/Python 3.12.12/NumPy 2.3.5). +Local wheel SHA256: +61fa4f6f4c16b02711e1541aee8b1aa5df6445cdf94341da2a59ee81e569ba7a. + +Observation: shared feature preparation and independent model state can be +separated without a global cache. Evidence: supplying one prepared object gives +the same per-run state as independently fitted preparation under reordered and +regrouped execution. Decision: reuse by explicit immutable identity/config +contract; never infer equivalence from shape or object address. + +This does not cache validation/inference transformations, establish measured +speedup or implement fusion. M32 equivalence here uses independent fixed round +budgets, not validation-triggered stopping. Next add independent stopping and +verify different stop rounds plus failure/reordering under shared preparation. +Other Sprint 035 requirements and F0.3/F1–F5 remain open. + +Status: complete for the bounded slice. See +[learning record](../learnings/2026-09-06-v1-shared-preparation.md). diff --git a/v1-sprints/038-goal-progress-and-plan.md b/v1-sprints/038-goal-progress-and-plan.md new file mode 100644 index 0000000..c2aec4e --- /dev/null +++ b/v1-sprints/038-goal-progress-and-plan.md @@ -0,0 +1,314 @@ +# Sprint 038: Goal, progress and remaining execution plan + +Reviewed revision: `8afce35`, clean branch `codex/gpu-python-foundation-design`. +Date: 2026-09-06. Status: review complete. M1 is delivered in +[Sprint 039](039-independent-stopping.md). M2 has installed D2/D3 development +evidence in [Sprint 040](040-installed-extensions.md) and ordered D4 evidence in +[Sprint 041](041-ordered-updates.md). [Sprint 042](042-result-contract.md) closes +external result interoperability. [Sprint 043](043-expectile-extension.md) adds +installed D1 expectile evidence. [Sprint 044](044-current-worker.md) starts M3 with +current A1/A11 validation workers; [Sprint 045](045-multioutput-worker.md) adds A6 +scale-bound validation integration. [Sprint 046](046-scale-bound-selection.md) +adds scale-bound selection and a synthetic current search. [Sprint 047](047-multioutput-quality.md) +adds standardized A6 quality reporting. [Sprint 048](048-classification-workers.md) +adds classification adapters and Adult validation. [Sprint 049](049-covertype-worker.md) +records five Covertype worker timeouts: profile this full-data CPU path next, +before additional adapters. Remaining M2 and M3–M6 are open. +This updates execution priorities after Sprints 036–037. It preserves the +[main plan](../planning/agent-boosting-foundation-plan.md), +[construction design](../planning/foundation-construction-design.md), all +R1–R9/C1–C7/A1–A13 requirements and the [E0–E7 gates](../planning/openboost-v1-evaluation.md). + +## Goal and assessment + +OpenBoost is a **programmable boosting foundation for researchers and agents**. +Its value is reducing the total cost from an algorithm idea to a correct, +reusable implementation with trustworthy task results. Implementation, debugging, +verification, repeated training and inference all count toward that cost. + +The torch analogy describes the role of composable operations and explicit +state, not a requirement to build a general tensor framework. Readable Python +algorithm code and efficient device operations are means to this goal. A useful +foundation must allow changes to statistics, split decisions, leaf solvers, +parameter updates and scheduling without repeatedly modifying its core. + +Standard GBDT, distributional/NaturalBoost, Formula and train-many are consumers +that test the abstraction boundaries. All application families remain required; +insurance, survival or any other family cannot stand in for the others. An +incumbent-compatible objective alone is not evidence of a distinctive advantage. + +**Assessment:** the architecture has progressed from specifications to a working, +broad CPU foundation. Shared components now support structurally different +algorithms. We have not yet established cheaper independent authoring, competitive +real-data quality/cost, GPU execution, or repeated external use. The next investment +should test those hypotheses and close specific semantic gaps, rather than expand +the built-in objective catalog without new evidence. + +| Product hypothesis | Evidence so far | Missing deciding evidence | +|---|---|---| +| Useful modifications require deeper access | D1–D5 specify objective, candidate, leaf, ordered-update and scheduling changes; incumbent hooks/source paths are documented | Fair implementation attempts showing where existing paths are actually costly | +| Shared components express different algorithms correctly | Public CPU operations, three growth policies, scalar/vector/residual leaves and twelve recipes have focused correctness tests | Installed public-only mutations and complete CPU conformance ledger | +| Agents can make those changes with less work | Mathematical verifiers and development cards exist | Frozen E5 attempts, including failures, appropriate controls and sealed held-outs | +| Workflow benefits outweigh runtime and adoption costs | Explicit preparation reuse, model persistence and installed examples work locally | Real selected comparisons, end-to-end cost, clean delivery matrix and independent repeated use | + +## Progress and evidence boundary + +F0.1 specification and F0.2 independent references are delivered. F0.3 evaluation +preparation remains incomplete. CPU construction has advanced through B03–B10 +plus the A6/shared-preparation follow-ups; formal F1 exit has not passed. The +approved overlap is not a retroactive F0.3 pass. Close the outstanding preparation +ledger before interface freeze or formal comparisons. + +The last full regression in [Sprint 037](037-shared-preparation.md) reports +**735 passing tests**: 247 public implementation tests plus 488 reference and +evaluation tests. This review independently collected the 247 public tests; it +did not rerun the full regression. Sprint 037 also records Ruff, strict MkDocs, +offline wheel/sdist and 19 installed-wheel examples passing on macOS, +Python 3.12.12 and NumPy 2.3.5. These results do not establish other platforms, +real-data quality, speed or adoption. + +| Area | Implemented and exercised | Remaining boundary | +|---|---|---| +| Data and preparation | Owned numeric/mixed features, typed categories, weights, offsets, structure, class order, target/raw axes; explicit fitted preparation | Complete application provenance and evaluation binding; preparation reuse covers training binning/codes only | +| Tree foundation | Named additive fields, independent information channels, scoring/feasibility, routing; depthwise/best-first/symmetric growth; scalar/vector/routed residual leaves | Independent external authors must demonstrate replacement through installed public operations | +| Runtime | Immutable proposals and accepted/best models, explicit run identity, scoped RNG, rejection and failure isolation | Validation-driven patience/stop state; ordered update authoring; GPU residency/workspace/transfer semantics | +| Inference/artifacts | Validated raw/tree models, category/class metadata, AFT scale, frequency-severity roles and multi-output inverse scaling | Complete Normal/Formula dependencies, output units and evaluation metadata across workflows; broader clean-install matrix | +| Train-many | Sequential heterogeneous runs, shared prepared input, same-ID M=1/8/32 independent/reordered/regrouped equivalence and retained failures | Different validation stop rounds; real model selection; batching/fusion and measured full-set cost | +| Evaluation | Independent references, frozen data records, baseline workers, selection/integrity infrastructure and sealed held-out preparation | Current OpenBoost worker, full expected matrix, protocol enforcement and gate aggregation; formal author/quality/cost results | + +All application rows below describe CPU mechanics, **not E3 acceptance**. Detailed +earlier findings remain in [Sprint 035](035-cpu-coverage-audit.md); Sprints 036–037 +resolve its missing A6 recipe and repeated training-preparation findings. + +| Required application | Current CPU path | Remaining application evidence | +|---|---|---| +| A1 regression | Squared-error recipe and numeric/mixed trees | Frozen real fit/selection/inference and quality comparison | +| A2 binary | Logistic geometry, explicit class order and probabilities | Real classification/calibration workflow and complete output metadata | +| A3 multiclass | Joint vector trees and multiclass bounds | Real multiclass comparison and class-schema workflow | +| A4 ranking | Query-local pairwise/lambda geometry and NDCG selection | Real source/query binding, declared pair limitations and ranking comparison | +| A5 quantiles | Routed weighted quantile and penalized leaf solvers | Temporal real evaluation, quantile reporting and installed leaf mutation | +| A6 multi-output | Independent/shared trees, train-fitted scaling and persisted inverse transform | Real grouped splits, scale-bound selection and per-target/final quality | +| A7 counts | Poisson with explicit exposure and rate/count transforms | Real frequency/deviance workflow and units/provenance | +| A8 positive targets | Weighted Gamma mean regression | Real eligibility, weights, positive-target comparison and units | +| A9 aggregate targets | Fixed-power Tweedie plus frequency-severity composition | Raw join audit, joint selection and aggregate quality | +| A10 survival | Event/right-censored log-normal AFT, fixed scale and survival inference | Source/license closure, censored NLL, IPCW/calibration and scale protocol | +| A11 distributional | Joint ordinary/Fisher Normal updates | Ordered-update mutation, distribution outputs, proper scores and calibration | +| A12 structured | Saturation Formula, explicit structure and full GGN | External formula dependency, real task plus recovery/misspecification evidence | +| A13 model selection | Shared preparation with independent heterogeneous runs | Independent stopping, frozen real ensemble selection and full-set cost | + +**CUDA boosting is not implemented in the current v1 package.** RunContext +explicitly rejects non-CPU devices. Historical GPU work and baseline preflights +are useful evidence about their recorded revisions, not current v1 GPU results. + +## Reflection and design risks + +1. **Breadth has justified shared components; it has not proved authorability.** + Recipe loops currently repeat policy wiring, and `_configuration`/`_trials` + are private. An external author may still need substantial loop duplication. + Test D2/D3/D4 early, record the actual obstacle, and expose only the smallest + reusable operation that the evidence justifies. Do not preemptively build a + generic trainer framework or count another built-in implementation as proof. +2. **Best-model selection does not implement early stopping.** The current + recipes use fixed round budgets. M32 equality is useful, but does not verify + independent validation-driven stopping. This is the next concrete correctness + gap, not a performance optimization. +3. **Preparation reuse is narrower than end-to-end reuse.** Prediction still + transforms inputs, and proposal/acceptance paths recompute ensemble predictions. + These call paths are a possible cost/residency problem, not a measured speed + regression. Profile representative shapes before redesigning caches. Preserve + identity checks, rejection semantics and numerical equivalence if optimizing. +4. **Evaluation integration is now more valuable than more isolated smokes.** + The baseline worker does not run current OpenBoost. The global matrix and + independent gate aggregation remain incomplete. Reuse existing data/baseline + artifacts, connect current recipes and expose missing cells explicitly. +5. **Adoption must remain an independent test.** Internal agents and repository + authors cannot establish E7. An installable method package is a useful entry + point; genuine demand requires another author to make and reuse a method. + +## Execution plan and acceptance + +The milestones are dependency groups, not single large commits. Open a bounded +sprint for each independently verifiable slice. M2 exploratory trials and M3 +evaluation preparation should begin before all remaining CPU work is polished; +formal measurement still waits for its frozen prerequisites. M3 starts real-data +integration early, rather than postponing first contact until GPU completion. + +### M1 — Independent stopping (next implementation sprint) + +Mapping: R9/C4/A13, remaining F1.4 and D5 prerequisites. + +- Define public stop policy/state separately from model acceptance. Track logical + outer rounds, accepted-state version, trial attempts and stop reason distinctly. + A line-search rejection must leave accepted/best model, raw values and RNG + unchanged; individual backtracking trials must not consume patience. +- Observe the finite validation metric once per completed logical round, including + a round whose proposals all reject. Keep the existing smaller-is-better metric + contract. Specify positive patience, nonnegative finite min_delta, ties, initial + baseline and zero-round behavior. Validation chooses stopping/best snapshots; + training loss continues to govern the declared step-acceptance policy. +- Preserve strict best-model selection independently of the patience threshold. + Record the last qualifying improvement for min_delta; ties do not reset patience. + Return current and best models with explicit completed rounds and termination + reason. Nonfinite metric failures remain visible and isolated to the affected run. +- Apply the same stop operation to built-in and external loops. Avoid encoding + objective names in the generic runtime or scheduler. + +**Acceptance:** a focused counterexample fails before implementation because +stop state is absent. Hand-calculated metric sequences verify threshold, tie, +rejection and boundary behavior. Shared-preparation M=1/8/32 runs use heterogeneous +K=1/2, different actual validation stop rounds, failure/retry and stable IDs; +independent, reordered and regrouped executions agree in stop state, predictions, +best snapshots and RNG. Existing fixed-budget behavior remains explicitly tested. +This establishes semantics, not fusion or speed. + +### M2 — Public authoring and remaining CPU workflows + +Mapping: C2–C7, R6/R7/R9, F1.5/F1.6 and exploratory F2.1/B11. + +- First run D2 candidate-information and D3 penalized-leaf development tasks in + separate installable extension packages against the wheel. They exercise deeper + boundaries than another custom loss. Record public/private imports, core edits, + duplicated logic, assistance and failures. These are exploratory, not scored E5 + or independent-adoption results. +- Exercise D4 joint-to-ordered Normal updates using public transactions; the next + parameter reads the latest accepted state. Verify both orders, strict finite + training-loss descent, at most six declared step sizes and complete rejection. + Exercise changed update policy on Formula with its separate independent oracle. +- Complete D1 and D5 and all required public recipe/workflow entry points. + Declare Normal/Formula inference dependencies and application output units; + verify fresh-process persistence and prediction after training plugins are removed + where core inference should be independent of those plugins. +- If an extension needs private imports or core edits, retain that failed attempt, + repair the minimal boundary, then rerun the affected development checks. Do not + compensate by putting each author task into a new built-in trainer. + +**Acceptance:** all five E2 modification types execute through installed public +interfaces with independent math/state checks, zero required core edits/private +imports and no task-name branches in the generic runner. Two separate extension +packages and runnable A1–A13 CPU workflows produce reconstructible artifacts. +This is expressiveness evidence; comparative authoring cost remains M4. + +### M3 — Close evaluation preparation and connect real applications + +Mapping: B02/F0.3, C6/C7, B11 and early F4/E3 preparation. + +- Reconcile the [F0 sequencing ledger](017-f0-sequencing-audit.md) against current + artifacts. Close the A4 source/query path and A10 source-license gap; preserve + existing hashes and corrected provenance rather than repeating resolved work. +- Freeze the full required recipe/application/device/fold/method matrix, output + units, selection rules and environment/resource identities. Complete independent + gate aggregation: absent, failed, contaminated or unsupported required cells + cannot produce a passing gate. Test process budget and test-label boundaries, + including the A6 train-scale/selection binding identified in Sprint 017. +- Add a current OpenBoost worker using the existing frozen data and result schemas. + Start with a complete regression/Normal workflow and A6/A13 integration to expose + output and selection issues, then connect every remaining application row. These + are integration waves, not a reduction of required coverage. +- Each connected path must run train-only preparation, validation selection, + sealed test evaluation, export and fresh-process prediction. Compare identical + target units and splits; retain failures and all declared configurations. +- Run full quality searches as each task's protocol and recipe stabilize. Formal + results require five declared splits/seeds, 16 configurations per method and all + task-specific controls. Do not use four-round plumbing as quality evidence. + +**Acceptance:** F0.3 has an explicit closing ledger; judges reject missing and +polluted evidence; all A1–A13 have runnable current OpenBoost/comparator bindings. +E3 then closes per application using committed raw results, at least six independent +sources and the existing thresholds. Do not mark all E3 passed because an early +integration wave passes. Cohort and CUDA-environment obligations also remain in +F0.3 and must be closed before the corresponding formal measurements. + +### M4 — Freeze and test the central product hypothesis + +Mapping: formal F1 exit, F2/B11, E0/E1/E2/E5. + +- Audit CPU coverage and close E0/E1/E2 against actual public code, not reference + totals. Complete F0.3 before freezing interfaces/judges and scoring comparisons. +- Freeze agent model/reasoning, tools/docs/cache, task/comparator versions, budgets + and accounting. Smoke-test the attempt runner before running the full cohort. + Allow each incumbent its strongest appropriate hooks, outer loop or source path; + include Py-Boost where applicable. D1 is a control, not a presumed win. +- Run three attempts per task/arm, capped at 30 minutes or 20k generated tokens. + Preserve unsuccessful attempts at the full cap in time aggregates. Separate + exploratory runs, frozen development results and sealed H1/H2 evaluation. + The foundation designer must not inspect held-out task/verifier contents; use + the authorized independent evaluator. Tasks used in redesign lose held-out status. + +**Acceptance:** E5 requires at least 12/15 correct development attempts and 2/3 +per development task; at least 2/3 per held-out task and 4/6 overall; no core/private +dependencies; at least two deep-change types reduce capped median completion +time by 30% or more without fewer correct completions. Failure triggers a task, +documentation or abstraction review, not weaker opponents or hidden hints. + +### M5 — Verified GPU execution and end-to-end cost + +Mapping: F3/B12–B13, required CUDA R1/R4/R5/R6/R8 and R9, E1/E4. + +- Profile CPU call paths and design explicit residency/transfer boundaries while + earlier work proceeds. Implementation follows proven CPU semantics and F2 + interface revisions under the main phase order; exploratory profiling is not + permission to declare F1/F2 complete. +- Build one complete resident path, then every required CUDA recipe subset, + a nondefault author component and multiparameter adaptive updates. Verify + gradients, statistics, chosen splits, leaves, every round, prediction and final + metrics on real CUDA hardware. Report transfers, synchronization and fallback. +- After independent sequential parity, implement compatible shared execution and + evaluate M=1/8/32. Separate preparation-only savings from batching/fusion. Use + authorized Modal for bounded runs with frozen hardware and complete provenance. + +**Acceptance:** existing E1 tolerances and E4 gates apply. Two quality-matched +medium/large standard workloads need warm-fit medians at most 2x the fastest +qualifying GPU control; public composition/optimized ratio at most 1.25. M=1 +overhead is at most 10%; at least one of M=8/M=32 saves 20% full-set time and the +other is no more than 10% slower, with all required ensembles within that latter +limit. Include memory, startup, compilation, validation and inference costs; +standard CPU inference has its separate 2x gate. Historical P7 retains its original +threshold and is reported separately. A kernel benchmark cannot close this stage. + +### M6 — Complete real value, delivery and adoption evidence + +Mapping: F4/F5/B14, E3/E6/E7; continues the real-data work started in M3. + +- Finish every A1–A13 result, including distribution/survival proper scores and + calibration, Formula real/recovery/misspecification cases and real train-many + selection. Each application passes independently; no cross-task average can hide + a failure. Use the unchanged metric-specific E3 thresholds. +- Close CPU/CUDA clean-wheel and declared platform checks, all standard workflows, + two extension packages, inference dependency removal and artifact reconstruction. + Stabilize only contracts justified by these consumers and author experiments. +- Prepare a small install-to-baseline-to-mutation-to-verification package for + external authors. Contact requires user authorization. E7 needs two independent + authors, with one implementing their own method and reusing it on another task. + This trial may start after F2 revisions, before all GPU work finishes. + +**Acceptance:** engineering v1 means every required E0–E6 gate and individual +A1–A13 evidence pass. Adoption/impact remains unverified until E7 independently +passes. Finishing engineering does not justify an ecosystem or business-model claim. + +## Immediate work and decision checkpoints + +1. Implement and verify M1 stop semantics in the next bounded sprint. +2. Run the first installed D2/D3 development extensions; build the D4 public ordered + update path around observed needs, not a speculative abstraction. +3. Begin M3's expected-matrix/current-worker integration alongside these CPU + follow-ups. Deliver the first complete real-data workflow, then all required rows. + +At every sprint closure, three implementation commits, phase transition or +correctness counterexample: record which hypothesis gained evidence, which gate +remains open and what can now be removed from the critical path. Repeated core edits +mean the foundation needs redesign. Real quality failures stay visible and must +be resolved without dropping required applications. Poor GPU end-to-end cost +requires profiling and an explicit failed gate, not a kernel-only success claim. + +No additional built-in family should displace these deliverables unless it closes +an existing required contract or a concrete correctness/authoring counterexample. +No implementation, GPU run, outreach, push or publication is part of this review. + +## Review verification + +- Read public runtime/recipe/run call paths, sprint evidence and the required + design/task/evaluation contracts. Preserved sealed held-out contents. +- Collected `tests/v1/test_public*.py`: 247 tests, not a fresh pass claim. +- Checked changed Markdown links, strict documentation build and diff whitespace. + No production behavior changed; the last full regression remains Sprint 037. +- [Learning record](../learnings/2026-09-06-v1-goal-progress-review.md). diff --git a/v1-sprints/039-independent-stopping.md b/v1-sprints/039-independent-stopping.md new file mode 100644 index 0000000..d570dc3 --- /dev/null +++ b/v1-sprints/039-independent-stopping.md @@ -0,0 +1,57 @@ +# Sprint 039: Independent validation-driven stopping + +Parent: 7a61744. Status: complete for the bounded CPU slice. +Mapping: Sprint 038 M1, R9/C4/A13, F1.4 and D5 prerequisites. + +## Plan and acceptance + +1. Add an immutable public stop record with finite smaller-is-better validation + observations, optional positive patience, nonnegative min_delta and round budget. + Keep it separate from accepted model state and strict best-model selection. +2. Observe once after each logical recipe round, including full rejection, in all + twelve recipes. Return completed rounds and budget/patience termination reason. +3. Verify hand-calculated sequences and real M=1/8/32 heterogeneous stop isolation, + including reordering, regrouping, retry, failures and shared preparation. +4. Update public examples, run relevant regression/lint/docs/build, reflect and + commit locally. No GPU, fusion, speed or formal phase-exit claim. + +First failing test imports the missing public StopState. Patience compares each +observation with the last qualifying improvement; strict improvement must exceed +min_delta. Ties consume patience. Initial validation is the baseline and consumes +no round. Budget zero completes immediately. Patience wins the reason if both +limits are reached on the same round. Best-model selection retains its existing +strict minimum independently of the patience threshold. + +## Results and reflection + +StopState is a public immutable operation independent of AcceptedState. All twelve +recipes accept scalar patience/min_delta and return the final stop record in +FitResult. RunSpec already forwards these scalar options without a scheduler or +objective branch. Validation stopping observes accepted raw values once per outer +round; existing training-loss acceptance and strict validation best snapshots are +unchanged. Current and best models remain explicit in the result. + +Thirty new tests verify threshold/tie/budget sequences, invalid and nonfinite +observations, zero-budget behavior in every recipe, improving training with +worsening validation, strict best selection below min_delta, and six rejected +trials per round consuming only one patience observation. M=1/8/32 scalar/Normal +jobs with different real validation stop rounds agree with independently scanned +fixed-budget prefixes, reversed/regrouped runs and retries. Failed configuration +and nonfinite validation runs retain their errors; shared preparation never refits. + +CPU regression: 765 passed. Ruff, strict MkDocs, offline wheel/sdist and twenty +installed-wheel documentation examples pass on macOS/Python 3.12.12/NumPy 2.3.5. +Wheel SHA256 is recorded in the linked learning entry. + +Observation: stop progress and model commit progress are different clocks. +Evidence: two fully rejected rounds attempt twelve steps while accepted version +remains zero; two worsening-validation accepted rounds retain the initial best +model. Decision: keep a separate public stop operation, which external ordered +loops can call at their declared outer-round boundary. + +This verifies CPU semantics, not training resume, real selection, GPU/fused +execution or performance. Metric observation adds an explicit validation-score +evaluation over cached raw predictions; inference/metric cost remains a profiling +follow-up. Next execute Sprint 038 M2 installed D2/D3 extensions and ordered +updates, with M3 current OpenBoost real-data integration. Formal phase gates remain +open. See [learning record](../learnings/2026-09-06-v1-independent-stopping.md). diff --git a/v1-sprints/040-installed-extensions.md b/v1-sprints/040-installed-extensions.md new file mode 100644 index 0000000..b4d43dd --- /dev/null +++ b/v1-sprints/040-installed-extensions.md @@ -0,0 +1,57 @@ +# Sprint 040: Installed public D2/D3 development extensions + +Parent: 2f6c77a. Status: complete for the D2/D3 development slice. Mapping: Sprint 038 M2, C2/C3/C6, +exploratory F2.1 and partial E2/E6. This is repository-authored development work, +not independent authorship, E5 comparative cost, held-out evidence or adoption. + +## Plan and acceptance + +1. Build two separate extension wheels using installed public interfaces only: + independent cohort feasibility and a separately implemented penalized leaf solver. +2. Verify D2 against exhaustive feasible split enumeration and no-feasible cases; + verify D3 against breakpoint/stationary-point minimization and next-round effects. +3. Run multi-round recipes from an isolated installed environment, save mixed/ + missing-feature models, remove both extensions and verify fresh-process predictions. +4. Record artifacts, source/wheel hashes, failures and usability observations; + run regression/lint/docs and commit locally. No core modification is presumed. + +First failing check: import the nonexistent extension modules. Acceptance requires +both callbacks actually execute, zero private OpenBoost imports/core changes, +independent math checks and inference without training plugins. D1/D4/D5 and the +remaining E2/E6 requirements remain separate. + +## Results and reflection + +Two separately built packages implement custom cohort feasibility and an external +penalized leaf solver. They run through public learner/grower callbacks with zero +core changes or private imports. Core wheel hash remains identical to Sprint 039. + +D2's exhaustive oracle selects cut 1 while the unconstrained optimum is different; +depthwise, best-first and symmetric growth all choose the constrained cut. A +cohort-separated feature has no feasible split. Zero training weights do not +erase independent information and a foreign problem fails binding. + +D3's bisection solver matches independent breakpoint/stationary enumeration on +30 deterministic weighted fixtures (maximum absolute difference +8.881784197001252e-16), including repeated residuals and zero weights. Subgradient +containment and movement toward the anchor under stronger penalty pass. Three +rounds with routed mixed/missing-feature leaves match the original-row oracle; +the custom leaves change subsequent raw values. Both saved models preserve exact +predictions in a fresh interpreter after both extension packages are uninstalled. + +Verification: 767 CPU regression tests pass; the strengthened routed-leaf check +also passes its two focused tests and a fresh full installation verifier. Ruff +and strict MkDocs pass. Installed environment: macOS/Python 3.12.12/NumPy 2.3.5, +OpenBoost 1.0.0.dev0 and extension versions 0.1.0. Raw source/wheel/environment/ +command/output evidence is in [the artifact directory](../benchmarks/v1/evidence/installed-extensions-040/README.md). + +Observation: these two deep changes reuse existing semantic boundaries without +duplicating recipe loops. Friction: the leaf callback is supplied through a grower +adapter and the quantile must be aligned in recipe and solver configuration. +Decision: retain this observable usability cost; do not infer a need for a new +generic trainer from one development attempt. Neither package is an independent +author, and these results do not measure comparative task effort or adoption. + +Next: D4 ordered Normal/Formula update composition, remaining D1/D5 installed +evidence and current OpenBoost real-data integration. M2 and formal E2/E6 remain +incomplete. See [learning entry](../learnings/2026-09-06-v1-installed-extensions.md). diff --git a/v1-sprints/041-ordered-updates.md b/v1-sprints/041-ordered-updates.md new file mode 100644 index 0000000..1452f5e --- /dev/null +++ b/v1-sprints/041-ordered-updates.md @@ -0,0 +1,55 @@ +# Sprint 041: Public ordered parameter updates + +Parent: 6b2385f. Status: complete for the D4 development slice. Mapping: Sprint 038 M2, D4/R6/R7/C4. + +## Plan and acceptance + +1. Add an installed development package composing public objective geometry, + least-squares fields, trees and propose/preview/resolve transactions. +2. Recompute geometry after each accepted parameter; test both orders for Normal + ordinary/Fisher and Formula full-GGN directions over multiple rounds. +3. Compare independent reference updates, including coefficients and rejection, + verify outer-round stopping and fresh inference after plugin removal. +4. Record measured boundaries, run regression/lint/docs and commit locally. + +D4 uses at most six rates 0.1*0.5**j and strict finite training-loss descent. +Validation chooses best snapshots after each accepted substep; patience observes +once per outer round. No core change is presumed. This is exploratory development, +not scored E5 or full E2/E6. Existing joint recipes remain explicit alternatives. + +## Results and reflection + +The ob-ordered-updates wheel supplies an objective-independent ordered sweep/fit +loop with Normal ordinary/Fisher and Formula full-GGN convenience wrappers. It +uses public geometry, fields, trees, preparation, transactions and StopState; +no src/openboost changes or private imports were needed. + +Six installed cases cover both parameter orders for Normal natural/ordinary and +Formula, with three rounds each. Every accepted raw update and tried coefficient +matches a separate reference trace (maximum absolute raw difference +2.220446049250313e-16). The reference producer runs before isolated execution; +the installed extension receives traces, not reference code. Eight saved models +(D2/D3 plus these six) preserve exact raw predictions after all plugins are removed. + +Sixteen focused tests also cover nonzero offsets/weights, different results for +joint versus ordered and reversed order, full reversal/NaN rejection, rejection +of the first parameter followed by acceptance of the second, recovery at the +second step size, invalid order and outer-round patience. CPU regression: 783 +passed. Ruff, strict MkDocs and four-wheel installation/removal verification pass +on macOS/Python 3.12.12/NumPy 2.3.5. Core wheel hash still matches Sprint 039. + +Observation: ordered adaptive algorithms can be expressed with public transactions +and a short external loop, without copying a built-in recipe. Evidence: both +structurally different parameter geometries share the same sweep implementation. +Counterexample: run_many rejects OrderedResult because it requires concrete +FitResult; the installed checker retains this failure. Decision: fix the shared +result contract during D5 rather than adding objective-name scheduler branches or +pretending external scheduling already works. This prevents a full E2/F1 exit. + +Next: shared result interoperability and remaining D1/D5 installed tasks, then +current real-data worker integration. Normal/Formula complete output/dependency +workflows and formal author/quality/GPU gates remain open. These are internal +development trials, not independent authors or E5 wins. + +See [raw evidence](../benchmarks/v1/evidence/ordered-updates-041/README.md) and +[learning record](../learnings/2026-09-06-v1-ordered-updates.md). diff --git a/v1-sprints/042-result-contract.md b/v1-sprints/042-result-contract.md new file mode 100644 index 0000000..0ec4309 --- /dev/null +++ b/v1-sprints/042-result-contract.md @@ -0,0 +1,49 @@ +# Sprint 042: Shared recipe result contract + +Parent: b99376d. Status: complete for result interoperability. Mapping: Sprint 038 M2, D5/C4/C6. + +## Plan and acceptance + +1. Reproduce OrderedResult rejection with an external recipe, then replace the + scheduler's concrete built-in result dependency with a public structural contract. +2. Validate accepted-state identity, completed stop metadata and outer-round trace + count while leaving per-round diagnostic content owned by the recipe. +3. Verify heterogeneous built-in/ordered M=1/8/32 shared-preparation runs under + permutation, regrouping, retry, differing stop rounds and isolated malformed results. +4. Rebuild installed extension evidence, run regression/lint/docs and commit locally. + +No objective/type-name exceptions or conversion to a built-in result class. +Accepted parameter commits need not equal outer rounds. A successful result must +be complete; interrupted work must not masquerade as a successful outcome. +This closes the Sprint 041 interoperability counterexample, not complete E2/E5. + +## Results and reflection + +RecipeResult is a public structural protocol with accepted state, per-outer-round +steps and completed StopState. Runtime validation checks actual field types, +requested context/problem identities and trace length. The scheduler no longer +imports concrete recipe result classes or interprets their diagnostic payloads. +The existing external OrderedResult is returned unchanged, without an adapter, +subclass requirement or extension source modification. + +Eleven focused tests pass, including the formerly rejected external result, +seven malformed/unfinished/foreign cases and M=1/8/32 mixed independent executions. +Ordered recipes legitimately have two model commits per outer round. Shared input +does not refit; reversed/regrouped/retried runs preserve state, predictions, stopping +and scoped RNG. Failures remain visible without contaminating later runs. + +Installed four-wheel verification reruns D2/D3/D4 and adds mixed scheduling checks +at M=1/8/32. All six ordered cases match direct execution, and eight persisted +models retain exact predictions after all plugins are removed. Raw evidence is +in [recipe-results-042](../benchmarks/v1/evidence/recipe-results-042/README.md). +CPU regression: 794 passed. Ruff, strict MkDocs and offline sdist/wheel pass on +macOS/Python 3.12.12/NumPy 2.3.5. + +Observation: algorithm-owned trace records exposed unnecessary dependence on a +built-in result class. Decision: share only the state/completion/outer-round +contract, not a catalog of permissible diagnostics. This is a foundation revision +prompted by an exploratory failure; it is not a frozen zero-core-edit author win. +Earlier failed artifacts remain unchanged. Next D1 and remaining D5 author probes, +alongside current real-data integration. Full E2/E5/E6 and real task gates remain open. + +See [learning record](../learnings/2026-09-06-v1-result-contract.md). diff --git a/v1-sprints/043-expectile-extension.md b/v1-sprints/043-expectile-extension.md new file mode 100644 index 0000000..876169a --- /dev/null +++ b/v1-sprints/043-expectile-extension.md @@ -0,0 +1,45 @@ +# Sprint 043: External expectile objective + +Parent: b7fdb60. Status: complete for D1 development. Mapping: Sprint 038 M2, D1. + +## Plan and acceptance + +1. Add a separate installable expectile package using only public CPU operations. + Preserve weighted initialization, offsets, analytic derivatives and tau=.8. +2. Compare initialization to stationary-interval enumeration and two rounds to + independent exhaustive tree references; cover zero weights and residual signs. +3. Run isolated installed checks and fresh-process raw prediction after removing + the training plugin. Record source hashes and reproducible artifacts. +4. Run regression/lint/docs, record friction and limitations, commit locally. + +The first focused test is the missing external expectile implementation against +the existing D1 oracle. No core objective or task-specific runner branches are +planned. This is internal development evidence, not a timed E5 attempt or an +advantage over incumbent custom-objective hooks. Real-data integration remains next. + +## Results and reflection + +Completed as an external package with no core edits/private imports. Expectile +geometry validates scalar problems and honors weights once through newton. Base +initialization bisects the weighted derivative over active target-minus-offset +values; an independent stationary-interval oracle checks 32 asymmetric weighted +fixtures plus explicit offset/zero-mass cases. Derivative checks include positive, +negative and zero residuals and finite differences away from the kink. + +Independent exhaustive trees match both boosting rounds with nonzero offsets, +missing values and zero weights. The installed result also passes run_many's +structural contract. Five wheels install in an isolated environment; all earlier +D2/D3/D4 and scheduling checks rerun. Nine raw models preserve exact predictions +in a fresh interpreter after removal of all four training plugins. + +Verification: 799 CPU tests pass; Ruff and strict MkDocs pass. Installed offline +wheel verification passes on macOS x86_64, Python 3.12.12, NumPy 2.3.5 with one +BLAS/OpenMP thread. See [raw evidence](../benchmarks/v1/evidence/expectile-043/README.md) +and [learning](../learnings/2026-09-06-v1-expectile-extension.md). + +The author still writes a small loop to wire geometry, statistics, transactions +and stopping. This is documented friction, not a measured author-time failure. +D1 remains a control for incumbent-friendly custom objectives; this internal +implementation does not imply a comparative win. No new builtin objective or +trainer abstraction was needed. Remaining D5 probes and current real worker +integration are next; full E2/E5/E6, GPU, real quality and adoption remain open. diff --git a/v1-sprints/044-current-worker.md b/v1-sprints/044-current-worker.md new file mode 100644 index 0000000..522950e --- /dev/null +++ b/v1-sprints/044-current-worker.md @@ -0,0 +1,43 @@ +# Sprint 044: Current OpenBoost evaluation worker + +Parent: 03ff34e. Status: complete for first-wave validation integration. Mapping: Sprint 038 M3, A1/A11 first wave. + +## Plan and acceptance + +1. Add a bounded numeric CPU worker for current squared and Normal recipes. + Accept frozen train/validation packets, reject test arrays and unknown options. +2. Declare final/best selection and mean/scale output semantics in a JSON model + bundle. Check fresh-process prediction without training data or recipe imports. +3. Exercise frozen housing packets in bounded subprocesses, preserving source, + input and output hashes, failures and environment metadata. +4. Run focused negative/schema and direct-recipe parity tests, regression/lint, + document limits and commit locally. No search/test-score or speed claims. + +Smallest failing test: the current worker module is absent. All A1–A13 remain +required; this adapter does not close F0.3, E3 or the remaining D5 author checks. + +## Results and reflection + +The new current worker accepts A1 squared and A11 joint Normal, with explicit +validation labels and optional weights. Unknown inputs/options, CUDA and other +applications fail. It requires a one-thread process budget. Fixed-budget output +uses the final model; enabled patience selects the strict best validation snapshot. +Training metadata separates outer rounds, accepted commits and model identity. +The JSON evaluation bundle declares scalar mean or Normal mean/standard-deviation +semantics; a separate inference entry point needs neither recipes nor training data. + +Fifteen focused tests pass: direct-recipe parity for both tasks with/without +patience, fresh-process exact replay, corrupt semantics and nine unsupported or +contaminated input cases. All 814 CPU tests pass, along with Ruff and strict docs. +All ten real validation cells pass: A1/A11 across five frozen housing folds, +four rounds, depth two and 32 bins. Exact fresh-process validation predictions +match, and all Normal scales are positive. Raw results and provenance are in +[current-worker-044](../benchmarks/v1/evidence/current-worker-044/README.md). + +This resolves the missing current-implementation path into the evaluation harness. +It does not close a complete selected train/test workflow: no 16-configuration +search, test scoring, competitor comparison or formal OS label isolation ran. +The 32-bin/four-round settings are explicit plumbing choices, not the frozen +quality grid. No GPU or speed claim. Next extend A6/A13 scale/selection integration +and remaining application adapters alongside D5 and F0.3 judge/protocol closure. +See [learning](../learnings/2026-09-06-v1-current-worker.md). diff --git a/v1-sprints/045-multioutput-worker.md b/v1-sprints/045-multioutput-worker.md new file mode 100644 index 0000000..765e8ce --- /dev/null +++ b/v1-sprints/045-multioutput-worker.md @@ -0,0 +1,44 @@ +# Sprint 045: A6 scaling-bound evaluation worker + +Parent: 374ab87. Status: complete for A6 validation integration. Mapping: Sprint 038 M3, A6. + +## Plan and acceptance + +1. Reproduce unsupported A6, then connect shared/independent multi-output recipes. +2. Fit the frozen unweighted target-scale convention on training targets only; + use identical scaling for validation selection and persist inverse output units. +3. Test heterogeneous/constant targets, sample weights, shifted validation targets, + corrupt scale metadata and fresh-process predictions for both growth modes. +4. Run five frozen Parkinsons folds, compare scale metadata to the freeze, run + regression/lint/docs, record reflection and commit locally. + +This is four-round validation integration, not A6 quality acceptance or A13 search. +No test truth is scored. Existing A1/A11 behavior must remain unchanged. + +## Results and reflection + +A6 now composes current multi_squared (shared or independent trees) with public +TargetScale/MultiOutputModel. The benchmark scale is computed from training +population means/stds using the existing frozen preprocessing operation, then +applied to train and validation. We do not substitute public weighted scale fitting: +the evaluation protocol uses unweighted scales, while sample weights still affect +training/validation losses. This distinction is explicit in saved metadata. + +Original-unit output is preserved in a JSON evaluation bundle carrying validated +scale metadata. Twenty worker tests pass (five new), including both modes with +and without patience, widely different target units, constant channels, zero and +nonuniform weights, shifted validation targets and corrupt scale rejection. +Fresh-process predictions exactly match direct public recipe/wrapper execution. +All five real grouped Parkinsons folds pass and scale metadata exactly matches +the committed freeze. These runs use shared mode; independent mode is tested on +synthetic fixtures, not claimed as a five-fold real result. + +Full CPU regression: 819 passed. Ruff and strict MkDocs pass. Raw evidence: +[multi-worker-045](../benchmarks/v1/evidence/multi-worker-045/README.md). +See [learning](../learnings/2026-09-06-v1-multioutput-worker.md). + +The protocol mismatch was resolved at the adapter boundary without changing core +scaling semantics or weakening the freeze. This is validation integration, not +full A6 acceptance: four rounds/32 bins, no configuration search, test score, +comparison or OS label isolation. A13 selection, remaining application adapters, +D5 probes and F0.3 closure remain next. No GPU or performance claim. diff --git a/v1-sprints/046-scale-bound-selection.md b/v1-sprints/046-scale-bound-selection.md new file mode 100644 index 0000000..6006219 --- /dev/null +++ b/v1-sprints/046-scale-bound-selection.md @@ -0,0 +1,45 @@ +# Sprint 046: Scale-bound current model selection + +Parent: e83e7b5. Status: complete for scale-bound selection integration. Mapping: Sprint 038 M3, A6/A13. + +## Plan and acceptance + +1. Reproduce the judge accepting A6 without verified training normalization. +2. Require hashed row-aligned training targets and frozen scale, recompute the + scale, bind inverse-standard-deviation metric weights, and report mean + standardized RMSE rather than an arbitrarily normalized weighted score. +3. Execute a synthetic 16-configuration current A6 search, seal the independently + recomputed winner and predict with that model in a fresh process after release. +4. Test missing/forged/misaligned scale inputs and winner selection, run regression + and lint/docs, record evidence and commit locally. + +All trial failures remain visible and prevent a search receipt. This is an +integration experiment, not frozen E3/E5/E7 evidence or OS-enforced isolation. +Real full-grid searches and remaining application adapters remain open. + +## Results and reflection + +The initial counterexample demonstrated that an A6 search lacking any training +scale binding could pass. The judge now requires hashed training targets aligned +to training IDs and a hashed scale artifact, recomputes the population scale, +and checks inverse-std coefficients exactly. Selection reports mean standardized +RMSE. Its old denominator preserved within-fold ordering under fixed coefficients +but gave a differently scaled numeric score; the definition is now explicit. +A hand-built test shows target normalization changing the correct winner. + +Eight added tests cover missing scale binding, mean standardized scores, a +normalization-dependent winner and forged weights/scales/target rows/values/width. +The full CPU suite passes 827 tests. Ruff and strict docs pass. The new synthetic +current search runs all 16 distinct shared/independent configurations, records +all outcomes, independently selects openboost:15, seals/re-audits its receipt, +and predicts in a fresh process after feature release. Constant target output +remains exactly seven. Raw artifacts: +[current-selection-046](../benchmarks/v1/evidence/current-selection-046/README.md). + +This closes the selection-coefficient binding portion of Sprint 017's A6 gap; +final comparative quality reporting is still open. Hashes and row alignment +establish consistency with the trusted supplied training data, not its external +provenance. The original objective remains open: real complete searches across +all applications, test isolation, D5, GPU and author/adoption gates. This synthetic +run is neither formal A13 cost evidence nor 16 configurations on real data. +See [learning](../learnings/2026-09-06-v1-scale-bound-selection.md). diff --git a/v1-sprints/047-multioutput-quality.md b/v1-sprints/047-multioutput-quality.md new file mode 100644 index 0000000..753155b --- /dev/null +++ b/v1-sprints/047-multioutput-quality.md @@ -0,0 +1,34 @@ +# Sprint 047: A6 standardized quality reporting + +Parent: 8d8272e. Status: complete for A6 reporting semantics. Mapping: Sprint 038 M3, A6 reporting gap. + +## Plan and acceptance + +1. Reproduce missing standardized-average reporting for A6 quality artifacts. +2. Require hashed training rows/targets/scale for A6 comparisons; recompute the + scale and reject row overlap, corruption and shape mismatches. +3. Report mean standardized RMSE alongside every original-unit target metric. + Test scale effects, constant targets and an average hiding a target failure. +4. Run regression/lint/docs, record findings and commit locally. No new real + quality claim or automatic E3 pass; model-selection provenance remains separate. + +## Results and reflection + +A6 reporting now requires a hashed train-row/target/scale bundle and recomputes +the frozen unweighted population scale. It rejects missing support, row identity +mismatches, training/evaluation overlap and scale changes. The standardized RMSE +is the mean of original-unit per-target RMSEs divided by training std; constant +target std is one. All per-target metrics remain mandatory and independently gated. + +Five new tests exercise the formula, constant targets, normalization support and +a case where the standardized average improves but one target fails. The latter +still fails the A6 comparison. After fixing a fixture helper NameError, the initial +focused check reproduced the missing primary-metric support before implementation. +The full suite passes 832 CPU tests; focused artifact tests, Ruff and strict docs +pass. No new dataset runs or quality/performance claims were made in this slice. + +Together with Sprint 046 this closes the scale-binding/reporting implementation +gap documented in Sprint 017. It does not close real A6 quality, selection +provenance, all recipes/devices, F0.3 or E3: E3_pass remains false by design. +Next remaining application adapters and real search integration, alongside D5. +See [learning](../learnings/2026-09-06-v1-multioutput-quality.md). diff --git a/v1-sprints/048-classification-workers.md b/v1-sprints/048-classification-workers.md new file mode 100644 index 0000000..d1bb8f5 --- /dev/null +++ b/v1-sprints/048-classification-workers.md @@ -0,0 +1,38 @@ +# Sprint 048: Classification evaluation workers + +Parent: b430df0. Status: complete for classification adapter/Adult integration. Mapping: Sprint 038 M3, A2/A3. + +## Plan and acceptance + +1. Reproduce unsupported classification jobs and add current binary/multiclass + adapters with explicit canonical class counts and encoded targets. +2. Persist and validate class order: binary returns P(class=1), multiclass returns + columns in encoded class order. Reject malformed labels/schema/configuration. +3. Check weighted direct-recipe parity and fresh-process probabilities with/without + patience for both tasks; run all five frozen Adult folds for A2. +4. Run regression/lint/docs, record evidence and commit locally. A3 full Covertype + runs remain separate; no quality/search/calibration or GPU acceptance claims. + +## Results and reflection + +Current A2/A3 workers compose the existing binary/multiclass recipes with explicit +canonical class counts. They return probabilities in declared class order and +persist the schema. Restored predictions reject missing/reordered schemas. +Twelve new tests cover weighted direct parity with/without patience, fresh-process +probabilities, invalid counts/labels/missing classes and unsupported options. +All 844 CPU tests pass; Ruff and strict docs pass. + +The first real Adult run failed all five folds: packet source IDs are strings, +while public NumericData requires integer IDs. The adapter now validates and +preserves source IDs at the packet boundary and uses local integer indices for +execution. String-ID fresh-process cases exercise this fix. No core row-identity +contract changed. The rerun passes all five Adult folds with exact probability +replay and external row IDs preserved. Both failed and passing raw evidence are +retained in [classification-048](../benchmarks/v1/evidence/classification-048/README.md). + +This is why real packet integration matters beyond small fixtures: it exposed an +actual consumer boundary mismatch. A3 full Covertype runs remain pending; synthetic +A3 checks do not stand in for that required coverage. Next continue real A3 and +remaining application adapters/search integration alongside D5. Four-round probes +are not quality/calibration, search or performance acceptance. See +[learning](../learnings/2026-09-06-v1-classification-workers.md). diff --git a/v1-sprints/049-covertype-worker.md b/v1-sprints/049-covertype-worker.md new file mode 100644 index 0000000..bf3582b --- /dev/null +++ b/v1-sprints/049-covertype-worker.md @@ -0,0 +1,43 @@ +# Sprint 049: Full-fold Covertype validation integration + +Parent: 0def101. Status: experiment complete; A3 validation failed its time budget. Mapping: Sprint 038 M3, A3. + +## Plan and acceptance + +1. Run the current multiclass worker on all five frozen Covertype folds with + the existing four-round, depth-two, 32-bin, one-thread smoke configuration. +2. Retain failures and bounded process outcomes; verify seven-class probability + shape/order/normalization, row identity and exact fresh-process inference. +3. Record source/input/model/output hashes and scope; update sprint/learning + records and commit locally. Do not increase budgets silently to hide failures. + +The existing 90-second fit and 30-second replay caps apply. This is full-dataset +validation integration, not a full quality grid, speed or GPU acceptance result. + +## Results and reflection + +All five full Covertype folds timed out at the unchanged 90-second fit cap. +No model/prediction artifacts were produced, so probability and fresh-inference +checks could not run. **0/5 passed; A3 full-dataset validation remains incomplete.** +Worker logs are empty; the process records show timeout/group termination, not +an algorithmic assertion or a measured bottleneck. Raw jobs, source/data/fold +identities and all process outcomes are retained in +[covertype-049](../benchmarks/v1/evidence/covertype-049/README.md). +Source and available artifact hashes match. No core/worker code changed. + +This is a concrete cost counterexample to treating synthetic A3 tests as sufficient +consumer evidence. It is not a fair comparative speed result: the smoke budget +is fixed, no competitor ran, and memory was not capped. Source/preprocessing +verification precedes the timed worker; fit caps do not measure total export cost. +The full source has 581,012 rows and 54 features; all frozen folds were used. + +Next prioritize a bounded profile of the same full-data input. Separate setup, +binning, initialization, per-round tree building, raw prediction and transactions. +Static inspection shows repeated prediction-time binning but does not establish +it as the cause. Preserve semantics and use measured evidence before optimizing +or increasing budgets. This redirects the next bounded slice, not required scope. +Other applications, D5, full searches, GPU and adoption remain required. + +Latest CPU regression remains Sprint 048's 844 passes; this evidence-only slice +did not rerun it. Strict MkDocs and diff checks pass. See +[learning](../learnings/2026-09-06-v1-covertype-worker.md). diff --git a/v1-sprints/README.md b/v1-sprints/README.md new file mode 100644 index 0000000..2b260d7 --- /dev/null +++ b/v1-sprints/README.md @@ -0,0 +1,189 @@ +# OpenBoost v1 execution and reflection + +This directory records the user-requested sprint plans, results and reflections. The goal is +**to help researchers and agents make correct algorithm changes using public composable components, +and verify their cost and practical value.** + +Sources: [main plan](../planning/agent-boosting-foundation-plan.md), +[construction design](../planning/foundation-construction-design.md), [tasks](../planning/foundation-tasks.md), +[evaluation](../planning/openboost-v1-evaluation.md). These define scope/architecture/gates; this +folder manages execution, not a competing roadmap. R1–R9/C1–C7/A1–A13 all remain required. + +## Current execution position + +Current review and remaining plan: [Sprint 038](038-goal-progress-and-plan.md). +Public CPU components now cover twelve recipes, multi-output target scaling and +shared training preparation. The latest full regression records 844 passes; +this includes references/evaluation infrastructure and is not a phase gate. +Independent stopping is delivered in [Sprint 039](039-independent-stopping.md). +Installed D2/D3 development wheels pass in [Sprint 040](040-installed-extensions.md). +Ordered updates pass in [Sprint 041](041-ordered-updates.md). Shared result +interoperability passes in [Sprint 042](042-result-contract.md). D1 expectile passes in [Sprint 043](043-expectile-extension.md). Next: remaining +D5 author probes and additional real-data adapters. [Sprint 044](044-current-worker.md) +connects current A1/A11 workers to all five frozen housing folds. +[Sprint 045](045-multioutput-worker.md) adds scale-bound A6 Parkinsons folds. +[Sprint 046](046-scale-bound-selection.md) binds A6 selection to training scales +and runs a current 16-configuration synthetic search with sealed model release. +[Sprint 047](047-multioutput-quality.md) adds verified-scale A6 standardized +quality reporting while retaining every per-target gate. Next: remaining +application adapters, real search integration and D5 checks. +[Sprint 048](048-classification-workers.md) adds A2/A3 probability adapters and +five-fold Adult integration. [Sprint 049](049-covertype-worker.md) records all +five full Covertype folds timing out at 90 seconds. A3 validation remains +incomplete; next profile this CPU path before further adapter expansion. +CUDA, formal author comparisons, real application +acceptance and independent adoption remain open. + +The chronological entries below record status at each sprint's revision. Their +historical "next" statements are superseded by the current review. + +| Sprint | Plan mapping | Status | Deliverable and record | +|---|---|---|---| +| 001 | B01/F0.2 scalar/tree subset | Complete; 55 tests | [Scalar/tree references](001-scalar-tree-reference.md) | +| 002 | User-requested early production retirement | Complete; namespace/build/docs checked | [Clean v1 starting point](002-retire-legacy-production.md) | +| 003 | B01/F0.2 transforms/classification | Complete; see record | [Transforms/classification](003-data-classification-reference.md) | +| 004 | B01/F0.2 A4–A6 probes | Complete; 122 total tests | [Ranking/quantile/vector](004-ranking-quantile-vector-reference.md) | +| 005 | B01/F0.2 A7–A10 probes | Complete; 183 total tests | [Positive targets/AFT](005-positive-aft-reference.md) | +| 006 | B01/F0.2 A11/A12/D4 probes | Complete; 208 total tests | [Normal/Formula](006-normal-formula-reference.md) | +| 007 | B01/F0.2 identity/A13/D5 | Complete; 229 total tests | [Identity/runs](007-identity-runs-reference.md) | +| 008 | B01/F0.2 exact D1/D3/D4 | Complete; 255 total tests | [Author-task references](008-author-mutation-reference.md) | +| 009 | B01/F0.2 full mixed/vector growth | Complete; 279 total tests | [Mixed/vector trees](009-mixed-vector-growth-reference.md) | +| 010 | B01/F0.2 finite compositions and exit | Complete; 288 total tests | [Compositions and exit](010-reference-integration-exit.md) | +| 011 | B02/F0.3 integrity subset | Complete; 336 total tests | [Integrity judge](011-artifact-integrity-judge.md) | +| 012 | B02/F0.3 A5 data/date windows | Complete; 360 total tests | [Bike freeze](012-bike-data-freeze.md) | +| 013 | B02/F0.3 A1/A11 five splits | Complete; 380 total tests | [Housing splits](013-housing-five-splits.md) | +| 014 | B02/F0.3 A2 official test/stratification | Complete; 396 total tests | [Adult freeze](014-adult-data-freeze.md) | +| 015 | Cross-cutting English prose | Complete; no phase advancement | [English repository](015-english-repository.md) | + +At Sprint 015, B02/F0.3 frozen evaluation was next and public F1 construction had +not started. The subsequently approved overlap and CPU construction are recorded +below. References are not product implementation; phase exits remain evidence-based. + +## Execution rules + +1. Start each sprint with purpose, F/B/A/C/E mapping, an independent failing example, deliverables and acceptance. +2. Commit every independently verified slice; record commands/results/unverified scope/commit, not just file counts. +3. **Reflect at every sprint closure, every three implementation commits, phase transitions, + and architectural/correctness counterexamples.** Record it in the current sprint; one reflection can satisfy multiple triggers. +4. Record evidence/reasons and update design before changing architecture/order. Never silently + relax gates, discard failures, remove cases or switch to multi-GPU/feature catalog work. + Routine small fixes do not require replanning the whole project. +5. `learnings/` stores durable cross-sprint conclusions linking here; execution details live here. +6. All repository prose must be English, per the user's instruction. + +## Reflection checklist + +- Which difficulty in making a correct algorithm change did this reduce? If preparation only, which component does it justify? +- Does execution follow construction dependencies? Did old API restrictions, specialized trainers or premature optimization slip in? +- Is there independent math/state evidence? Which internal simulations cannot support performance, quality or adoption claims? +- Can structurally different cases reuse the boundary, or is it being designed around one example? +- What required scope remains? What is the next smallest verifiable deliverable? + +Use observation → evidence → decision → next step, not an unsupported conclusion that the direction is right. + +## Overall completion + +F0.1 specifications/construction and F0.2 references are delivered. F0.3 and F1–F5 remain incomplete. +Sprint 001 delivered scalar/tree; 002 retired production; 003 transforms/classification; 004 ranking/ +quantile/vector; 005 positive/count/policy joins/event-right-censored AFT; 006 Normal/Formula; +007 identity/isolation/selection; 008 D1/D3/D4; 009 full mixed/vector growth; 010 finite model/state +compositions. See [F0.2 exit mapping](f0-2-acceptance-ledger.md). + +Sprint 011 adds integrity judging. Sprint 012 freezes A5 data/calendar/five date +windows. Sprint 013 adds Housing inputs/five splits for A1/A11; its initially +unresolved license later received a source declaration, with provenance limits +recorded in the [review](../benchmarks/v1/datasets/housing-license-review.json). +Sprint 014 adds A2 Adult data and official-test-preserving splits. Further data, +baseline/budget, worker, selection and sealed held-out preparation exist; see the +Sprint 017 ledger and Sprint 038 for remaining integration obligations. All real +quality gates remain open. Creating these files did not pass E0–E6; E7 has no new +independent-adoption evidence. Every application requires individual acceptance. + +Current sequencing review: [Sprint 017 audit](017-f0-sequencing-audit.md). +It distinguishes existing F0.3 prerequisites from later evaluation results and +proposes a bounded CPU-construction overlap. The phase gate has not been changed. + +The user approved the Sprint 017 overlap on 2026-09-06. Active construction: +[Sprint 018 / B03](018-b03-cpu-state.md). F0.3 remains incomplete; no scope or +acceptance threshold was removed. + +[Sprint 019 / B04 operations](019-b04-numeric-operations.md) delivers numeric +preparation and shared scalar split operations. +[Sprint 020 / B04 trees](020-b04-depthwise-tree.md) adds depthwise assembly and +validated numeric tree inference/persistence. +[Sprint 021 / B05 squared](021-b05-squared-recipe.md) adds mapped tree transactions +and the first complete squared CPU recipe. +[Sprint 022 / B05 Normal](022-b05-normal-recipe.md) separates target/raw widths and +adds joint Normal ordinary/Fisher updates. +[Sprint 023 / B06](023-b06-formula-runs.md) adds saturation Formula/full GGN and +sequential heterogeneous execution probes. +[Sprint 024 / B07 growth](024-b07-growth-policies.md) adds best-first and symmetric +numeric policies. [Sprint 025 / B07 categories](025-b07-categorical.md) adds mixed +input, category equality and typed dictionary persistence. +[Sprint 026 / B08 binary](026-b08-binary.md) adds explicit class order and binary +logistic inference/training. +[Sprint 027 / B08 vectors](027-b08-vector-multiclass.md) adds joint multiclass, +vector leaves, separate split/leaf statistics and output mappings (630 tests). +B09 ranking/quantile/penalized leaves are next; full A6 workflows, CUDA and +quality/performance evaluation remain incomplete. + +[Sprint 028 / B09 ranking](028-b09-ranking.md) adds query-local pairwise/lambda +geometry and fixed-step ranking (642 tests). Quantile/penalized routed leaves +are next; real A4 evaluation and the broader incomplete gates remain open. + +[Sprint 029 / B09 quantiles](029-b09-quantile-leaves.md) adds routed residual +views, weighted quantile and anchored penalized leaves (655 tests). B10 positive +target/exposure and AFT construction is next; real application gates remain open. + +[Sprint 030 / B10 Poisson](030-b10-poisson.md) adds explicit exposure/count +geometry and rate/count inference (665 tests). Gamma/A8 is next, followed by +Tweedie/composition/A9 and AFT/A10. Real A7 evaluation remains open. + +[Sprint 031 / B10 Gamma](031-b10-gamma.md) adds weighted positive-target means +(674 tests). Tweedie/frequency-severity composition and AFT are next; real A8 +evaluation remains open. + +[Sprint 032 / B10 Tweedie](032-b10-tweedie.md) adds fixed-power nonnegative +means (684 tests). Frequency-severity composition and AFT are next; real A9 +quality and complete application artifacts remain open. + +[Sprint 033 / B10 composition](033-b10-frequency-severity.md) adds matched +paid-loss problems and persisted two-model inference (693 tests). AFT is next; +real A9 joins/quality and joint selection remain open. + +[Sprint 034 / B10 AFT](034-b10-aft.md) adds explicit event/right-censored +targets and persisted fixed-scale survival inference (716 tests). Next audit +CPU/B11 coverage; full A6, extension tasks, real application results and CUDA +remain incomplete. + +[Sprint 035 / CPU coverage audit](035-cpu-coverage-audit.md) maps all required +applications/capabilities and orders remaining work. Public production subset: +228 passing tests. Next A6 multi-output regression, then shared preparation and +independent stopping/M32. F1/B11 prerequisites are not complete. + +[Sprint 036 / A6 multi-output](036-a6-multioutput.md) closes the independent/ +shared recipe and persisted target-scaling gap (730 tests). Next: shared +preparation and independent validation-driven stopping/M32. Real A6 remains open. + +[Sprint 037 / shared preparation](037-shared-preparation.md) reuses training +binning/codes across independent M1/8/32 runs (735 tests). Next: independent +validation-driven stopping; prediction-time caching and performance remain open. + +[Sprint 039 / independent stopping](039-independent-stopping.md) adds public +validation patience across all twelve recipes, separate from transaction state, +with M=1/8/32 heterogeneous stopping/failure/retry equivalence. Next: installed +public D2/D3 extensions, ordered updates and current real-data integration. + +[Sprint 040 / installed extensions](040-installed-extensions.md) verifies public +cohort-feasibility and penalized-leaf packages without core changes, including +independent math checks and fresh-process inference after plugin removal. This is +partial E2/E6 development evidence; formal agent/adoption results remain open. + +[Sprint 041 / ordered updates](041-ordered-updates.md) compares both Normal and +Formula update orders with independent references and verifies installed execution +and inference after plugin removal. The OrderedResult/run_many incompatibility is +retained as the next D5 integration counterexample. No core edits or E5 claim. + +[Sprint 042 / result contract](042-result-contract.md) resolves the counterexample +with structural validation of completed results and installed mixed M=1/8/32 +equivalence. External result types and their diagnostic payloads are preserved. diff --git a/v1-sprints/f0-2-acceptance-ledger.md b/v1-sprints/f0-2-acceptance-ledger.md new file mode 100644 index 0000000..1b228ec --- /dev/null +++ b/v1-sprints/f0-2-acceptance-ledger.md @@ -0,0 +1,50 @@ +# F0.2 independent reference acceptance mapping + +As of Sprint 010: reference evidence only. **F0.2 closed; F0.3 not yet executed; F1 not started** +at this audit. Sources: [tasks](../planning/foundation-tasks.md), [plan](../planning/agent-boosting-foundation-plan.md). +Existing evidence here does not mean the corresponding A/R/C or E-gate passed. + +## Application references + +| Scope | Independent evidence | Remaining work | +|---|---|---| +| A1/R1 | Sprint 001 scalar math, three growth policies, two rounds | Public conformance/external differences in F1/F0.3 | +| A2/A3/R1 | Sprint 003 classification; 009 full native categorical growth/raw-transform two rounds | Public components/real quality in F1/F4 | +| A4/R3 | Sprint 004 pair/query normalization, lambda, two rounds, NDCG | Real query evaluation/sampling protocol; not a F0.2 speed task | +| A5/R2 | Sprint 004 three weighted quantiles/routed leaves/two rounds; 008 penalty; 010 composed predictions | Persistence/public components in F1 | +| A6/R8 | Sprint 004 stump; 009 three-policy multilevel vector/projection/K=1/two rounds | Public components/real quality in F1/F4; retain both structures | +| A7/A8/R4 | Sprint 005 Poisson/Gamma/exposure/weights/two rounds; 010 offset once/new exposure | Real adapters in F0.3 | +| A9/R4 | Sprint 005 Tweedie/paid-count joins; 010 two-round Poisson/Gamma fits and product | Real quality F4, persistence F1 | +| A10/R5 | Sprint 005 event/censoring, tails, units, two rounds | Output/persistence later; real IPCW fixed in F0.3 | +| A11/R6 | Sprint 006 Fisher/ordinary, NLL/CRPS, joint/ordered; 008 exact D4; 010 versioned acceptance/best | Public runtime/real quality F1/F4 | +| A12/R7 | Sprint 006 Jacobian/GGN, three directions, repeated Z, two rounds, misspecification/nonidentifiability | Real quality/formula artifacts F1/F4; synthetic parameters do not prove physical truth | +| A13/R9 | Sprint 007 K=1/2 isolation, M=1/8/32 sequential, best/failures/RNG | Sequential simulation does not prove batching, cost or multiple-recipe integration | + +## Author changes and cross-case boundaries + +| Scope | Evidence and next step | +|---|---| +| D1 expectile | Sprint 008 tau=.8, weighted base, two rounds, tau=.5 reduction, r=0 convention | +| D2 cohort split | Sprint 001 candidate feasibility, no legal split, independent information weights | +| D3 penalized quantile | Sprint 008 breakpoint/stationary enumeration, subgradient, anchor/lambda, two-round routed leaves | +| D4 ordered acceptance | Sprint 008 six alphas, reverse/NaN rejection; 010 two-round commits, best, logical-step keys | +| D5 scheduling | Sprint 007 independent/sequential/reordered/regrouped, retry, stop, ID seed/content changes | +| C1 identity/bind | Sprint 009 mixed transforms/fitted identity/raw prediction; production typed contracts F1 | +| C2/C3 tree/leaf | Scalar, D3, categories, multilevel vectors; not production conformance | +| C4 state/run | Sprint 007/009/010 isolation, mapping, offset/two-stage, versioned commit, best caches/terms; runtime F1 | +| C5 artifacts | Serialization round trips are F1 construction; memory snapshots are not persistence evidence | +| C6/C7 eval/workflow | F0.3 freeze/judge, F2 author evaluation, F5 installed workflows incomplete | + +## Exit audit and next phase + +1. Sprint 008 supplies exact D1/D3/D4 definitions/counterexamples, not E5 author-cost evaluation. +2. Sprint 009 supplies categories/multilevel vectors/mixed data; 010 supplies offset/dual-model/finite state compositions. +3. F0.2 preparation is complete: 288 tests pass and references run with openboost imports blocked. + Do not mark F1 requirements passed early. +4. Begin real manifests, capability smoke, budgets/held-out tasks/judge freezing in F0.3. + +R1–R9/C1–C7/A1–A13 all remain required. Optional GPU entries never make CPU applications optional. +Commands/counterexamples/reflection: [Sprint 010](010-reference-integration-exit.md). +Mathematics: `tests/v1/test_{scalar,tree,data,classification,extended,positive_aft,coupled,runs,author,mixed,integration}_reference.py`. +Isolation: `tests/v1/test_reference_independence.py`. All evidence is tiny CPU reference work, +not external-library equivalence, real quality, serialization, author-cost or CUDA correctness.