Proof-carrying tabular prediction without pretrained weights.
Important
TabPVN is source-available for research and evaluation only. Production use, commercial use, and operational decision-making require separate written permission. This is not an open-source license. See LICENSE.
Start with the differentiator: inspect a proof-carrying reply.
TabPVN is a deterministic, self-configuring estimator for tabular classification and regression. It combines statistical proposers with a symbolic verifier so a prediction can be returned with a machine-checkable account of how the fitted model produced it.
The research question is simple: how far can tabular prediction be pushed with explicit programs, leak-safe validation, and verifiable arithmetic instead of a pretrained black-box prior?
TabPVN uses no hosted model, pretrained checkpoint, or training-time neural
network. The default fit path automatically handles numeric and categorical
columns, missing values, raw UTF-8 byte fields, imbalanced targets, and repeated
entity-event tables. Byte fields are processed without a learned token vocabulary
or stored-row retrieval. Candidate components are deployed only when bounded
out-of-fold or future-window evidence admits them.
TabPVN proofs verify model execution and declared statistical evidence. They do not guarantee that an unknown individual label or target is correct.
TabPVN introduces a Proposer-Verifier Network (PVN) for tabular learning. A PVN treats model construction as validated program synthesis rather than a single opaque optimization step:
- Proposers generate explicit candidate programs. Candidates include additive threshold regions, categorical posteriors, numeric intervals, affine reads, compression evidence, and causal temporal state.
- A verifier assigns bounded authority. Each candidate is measured on training-only out-of-fold predictions, or on past-to-future windows for event data. It may receive ranking authority, decision authority, both, or neither.
- The selected program carries its execution proof. Prediction conditions and arithmetic can be replayed independently by the small symbolic kernel.
- The default is the research method. Architecture selection, imbalance handling, calibration, and scale budgets are automatic; dataset-specific tuning is not required.
This separates two questions that conventional estimators usually combine: what structure might predict well? and what evidence permits that structure to affect a result? The proposer can be aggressive; the verifier remains the deployment boundary.
| Property | TabPVN research path | Conventional fitted predictor |
|---|---|---|
| Candidate search | Multiple explicit, bounded program families | One configured model family |
| Admission | Out-of-fold or future-window transfer evidence | Training objective and configured regularization |
| Authority | Separate ranking and decision permissions | Predictor output is authoritative by default |
| Prediction evidence | Replayable conditions, arithmetic, and audit binding | Model-specific inspection or post-hoc explanation |
| User configuration | Automatic defaults with advanced overrides | Commonly selected through tuning |
A normal regressor returns a number. TabPVN returns the number together with a validated error bound, replayable conditions, and a machine-checkable audit binding.
This example uses the real
UCI/NASA Airfoil Self-Noise dataset,
whose target is scaled sound pressure in decibels. It uses official TabArena
fold 0 and the automatic TabPVN() defaults, with no dataset-specific tuning.
| Official fold-0 result | Value |
|---|---|
| Training / held-out rows | 1,002 / 501 |
| TabPVN RMSE | 1.2879 dB |
| Histogram GBDT RMSE | 1.5462 dB |
| Relative RMSE reduction | 16.7% |
The following held-out row was selected for a compact proof with readable
physical conditions. It is an illustrative response, not the aggregate result
above. Reproducing it requires the openml optional dependency:
from benchmark.datasets import tabarena_suite
from tabpvn import TabPVN
dataset = tabarena_suite(dataset_names=["airfoil_self_noise"])[0]
train_indices, test_indices = dataset.splits[0]
model = TabPVN(task="regression").fit(
dataset.X.iloc[train_indices],
dataset.y[train_indices],
)
held_out_row = 355
row = dataset.X.iloc[[test_indices[held_out_row]]]Input:
{
"frequency": 4000,
"attack-angle": "15.4",
"chord-length": 0.0508,
"free-stream-velocity": 55.5,
"suction-side-displacement-thickness": 0.0271925
}The public reply predicts 119.943 dB and returns a validated interval of [117.634, 122.252] dB. Its first replayable reason requires all of these observed conditions:
| Feature | Verified condition | Observed |
|---|---|---|
| Frequency | 2500 < x <= 5000 |
4000 Hz |
| Displacement thickness | x > 0.00592927 |
0.0271925 m |
| Chord length | x > 0.0254 |
0.0508 m |
| Free-stream velocity | x > 39.6 |
55.5 m/s |
The target was not supplied when the reply was built. Afterward, the held-out target was revealed as 120.920 dB, an absolute error of 0.977 dB, inside the declared interval.
Complete public tabpvn.proof/3 response
{
"schema": "tabpvn.proof/3",
"summary": "Prediction: 119.943. Decision verification passed. Validated error bound: +/- 2.30923. No observed target was supplied.",
"prediction": {
"task": "regression",
"value": 119.94294784614738
},
"reliability": {
"status": "verified",
"type": "error_bound",
"value": 2.3092268354244765,
"applies_to": "validated_population",
"interval": [
117.6337210107229,
122.25217468157186
]
},
"reasons": [
{
"conditions": [
{
"feature": "frequency",
"operator": "gt",
"value": 2500.0,
"observed": 4000.0
},
{
"feature": "frequency",
"operator": "lte",
"value": 5000.0,
"observed": 4000.0
},
{
"feature": "suction-side-displacement-thickness",
"operator": "gt",
"value": 0.00592927,
"observed": 0.0271925
},
{
"feature": "chord-length",
"operator": "gt",
"value": 0.0254,
"observed": 0.0508
},
{
"feature": "free-stream-velocity",
"operator": "gt",
"value": 39.6,
"observed": 55.5
}
],
"supports": 119.94294784614738
},
{
"conditions": [
{
"feature": "frequency",
"operator": "gt",
"value": 3150.0,
"observed": 4000.0
},
{
"feature": "frequency",
"operator": "lte",
"value": 5000.0,
"observed": 4000.0
},
{
"feature": "suction-side-displacement-thickness",
"operator": "gt",
"value": 0.00544854,
"observed": 0.0271925
},
{
"feature": "chord-length",
"operator": "gt",
"value": 0.0254,
"observed": 0.0508
}
],
"supports": 119.94294784614738
},
{
"conditions": [
{
"feature": "frequency",
"operator": "gt",
"value": 2500.0,
"observed": 4000.0
},
{
"feature": "frequency",
"operator": "lte",
"value": 5000.0,
"observed": 4000.0
},
{
"feature": "suction-side-displacement-thickness",
"operator": "gt",
"value": 0.00461377,
"observed": 0.0271925
},
{
"feature": "chord-length",
"operator": "lte",
"value": 0.0508,
"observed": 0.0508
}
],
"supports": 119.94294784614738
},
{
"conditions": [
{
"feature": "frequency",
"operator": "gt",
"value": 3150.0,
"observed": 4000.0
},
{
"feature": "frequency",
"operator": "lte",
"value": 8000.0,
"observed": 4000.0
},
{
"feature": "suction-side-displacement-thickness",
"operator": "gt",
"value": 0.00251435,
"observed": 0.0271925
},
{
"feature": "chord-length",
"operator": "gt",
"value": 0.0254,
"observed": 0.0508
}
],
"supports": 119.94294784614738
},
{
"conditions": [
{
"feature": "frequency",
"operator": "gt",
"value": 2500.0,
"observed": 4000.0
},
{
"feature": "frequency",
"operator": "lte",
"value": 4000.0,
"observed": 4000.0
},
{
"feature": "suction-side-displacement-thickness",
"operator": "gt",
"value": 0.00566229,
"observed": 0.0271925
},
{
"feature": "chord-length",
"operator": "gt",
"value": 0.0254,
"observed": 0.0508
}
],
"supports": 119.94294784614738
}
],
"outcome": {
"status": "not_observed"
},
"verification": {
"status": "verified",
"decision": "verified",
"reliability": "verified",
"audit_reference": "sha256:8daf0cf6862e03c4e62d329cf248d38353c01e8a81773de0ed5255084429c293"
}
}This is model execution evidence, not post-hoc feature importance. The audit reference binds the unmodified public reply to its detailed artifact. The artifact verifies without fitted model state, and changing the prediction makes verification fail:
from copy import deepcopy
from tabpvn import TabPVN
reply = model.proof(row, row=0)
artifact = model.proof_artifact(row, row=0)
assert TabPVN.check_proof(artifact)
assert TabPVN.check_proof(reply, artifact=artifact)
tampered = deepcopy(reply)
tampered["prediction"]["value"] = 0.0
assert not TabPVN.check_proof(tampered, artifact=artifact)
lower, upper = reply["reliability"]["interval"]
observed_target = dataset.y[test_indices[held_out_row]]
assert lower <= observed_target <= upperThe distinction is deliberate: verification proves that the disclosed reasons and arithmetic reproduce the model response and its fitted reliability claim. It does not claim access to the unknown ground-truth outcome.
These results are research snapshots, not a claim that TabPVN is state of the art. They report fixed protocols and include the negative primary-comparator result.
The last completed packaged-default promotion run used official TabArena v0.1 fold 0 for all 51 tasks. Classification was scored by ROC AUC (macro one-vs-one for multiclass); regression was scored by RMSE. Each model used the same task split, and a win means higher AUC or lower RMSE.
| Fold-0 result | TabPVN | Histogram GBDT |
|---|---|---|
| Average rank across 51 tasks | 1.235 | 1.765 |
| Task wins | 39 / 51 | 12 / 51 |
| Classification wins | 26 / 38 | 12 / 38 |
| Regression wins | 13 / 13 | 0 / 13 |
Across the 38 classification tasks, the mean paired difference was +0.0079 ROC AUC with a 95% normal-approximation interval of [+0.0038, +0.0121]. Across the 13 regression tasks, the mean paired reduction in RMSE was 5.45%, with a 95% normal-approximation interval of [2.66%, 8.25%].
This is a one-fold architecture-promotion result. It is useful evidence against the fixed GBDT baseline, but it is not enough for a general superiority claim.
A bounded CPU check covered four official fold-0 classification tasks. TabPVN did not beat TabPFN-3 on this slice:
| Dataset | TabPVN ROC AUC | TabPFN-3 ROC AUC |
|---|---|---|
maternal_health_risk |
0.9464 | 0.9652 |
credit-g |
0.7619 | 0.7824 |
qsar-biodeg |
0.9229 | 0.9318 |
taiwanese_bankruptcy_prediction |
0.9358 | 0.9509 |
The current evidence therefore supports "competitive with a strong fixed GBDT baseline on this protocol," not "better than TabPFN-3" or "best in the world." TabPFN-3 remains the primary research target.
The completed Kaggle HIGGS scale audit used 1,000,000 training rows, 50,000 test rows, and 28 numeric features. On an Apple M1 Pro with 16 GB RAM, the default TabPVN path recorded:
| Metric | Result |
|---|---|
| ROC AUC | 0.8284 |
| Accuracy | 0.7474 |
| Log loss | 0.5064 |
| Fit time | 101.4 s |
| Probability inference, 50K rows | 1.52 s |
| Peak resident memory | 0.84 GiB |
This run demonstrates bounded million-row execution; it did not include a same-run competitor and therefore is not an accuracy comparison. The wider BeyondArena 1M+ audit also exposed unresolved memory and timeout limits on wide tables, which remain active research work.
Results were produced on macOS with Python 3.11, NumPy 2.4, and scikit-learn
1.6. The Arena summary corresponds to implementation fingerprint
43b88eec9ae1d545; the HIGGS snapshot was recorded on 2026-07-16. Regenerate
results after implementation or dependency changes before making comparative
claims.
flowchart LR
A[Raw table] --> B[Schema compiler]
B --> C[Statistical proposers]
C --> D[Leak-safe validation gates]
D --> E[Selected predictor]
E --> F[Prediction]
E --> G[Symbolic verifier]
G --> H[Public proof response]
G --> I[Audit artifact]
- Compile the table. Raw pandas and NumPy inputs become deterministic numeric, categorical, missingness, byte-sequence, relation, or temporal facts.
- Propose bounded improvements. The certified additive booster can be complemented by explicit categorical posteriors, numeric interval evidence, affine reads, compression evidence, or causal temporal state.
- Require transferable evidence. A challenger receives decision or ranking authority only after passing the relevant held-out protocol.
- Verify the result. The proof kernel independently replays the selected rules and arithmetic.
- Separate public and audit surfaces.
proof()returns a stable, implementation-neutral response.proof_artifact()returns detailed material only when an auditor explicitly requests it.
| Area | Current behavior |
|---|---|
| Classification | Binary and multiclass prediction with calibrated probabilities |
| Regression | Point prediction with conformal error bounds when calibration is available |
| Raw schemas | pandas and NumPy inputs, categoricals, missing values, datetime, and bounded UTF-8 byte evidence without tokenization |
| Imbalance | Rare-event sampling, average-precision gates, and explicit operating points |
| Event tables | Automatic entity/time discovery with causal future-window validation |
| Explanations | Typed conditions, sufficient reasons, stability, recourse, and proof artifacts |
| Decisions | Prior-shift correction, cost-derived abstention, and no-arbitrage checks |
| Persistence | Atomic, versioned model and named SDK-run save and load |
| Integration | sklearn-style estimators plus tidy notebook batches and JSONL proof-receipt export |
TabPVN currently supports Python 3.11 and 3.12.
git clone git@github.com:lenileiro/TabPVN.git
cd TabPVN
uv syncFor an editable installation without uv:
python -m pip install -e .Optional dependency groups are installed only when needed:
uv sync --extra dev # pytest, Ruff, mypy, and vulture
uv sync --extra openml # OpenML and TabArena datasets
uv sync --extra gbdt # XGBoost, LightGBM, and CatBoost baselines
uv sync --extra pfn # TabPFN comparison backend
uv sync --extra benchmark # large-data benchmark dependencies
uv sync --extra attest # signed target attestationsTabPVN remains the sklearn-compatible estimator. fit_run() and TabPVNRun
add the thin workflow layer needed in notebooks and training pipelines: a stable
label, JSON metadata, tidy batch results, selective proof generation, and
one-file run persistence.
label identifies one fitted model lifecycle. Use a value that remains meaningful
in exported predictions and saved artifacts, such as churn-v1,
lifetime-value-2026q2, or fraud-production-candidate.
from tabpvn import fit_run
run = fit_run(
X_train,
y_train,
label="churn-v1",
metadata={"owner": "retention", "training_window": "2026-Q2"},
)
assert run.label == "churn-v1"
assert run.task == "classification" # inferred from yMetadata must be JSON-serializable. It is carried into run.summary() and the
saved run artifact, but it does not affect fitting. Classification versus
regression is inferred from y; pass task="classification" or
task="regression" only when an explicit task is necessary.
The equivalent two-step form is useful when constructing runs dynamically:
from tabpvn import TabPVNRun
run = TabPVNRun(label="churn-v1", metadata={"owner": "retention"})
run.fit(X_train, y_train)The wrapper owns the label and metadata. The fitted sklearn-compatible estimator
remains available through run.estimator.
summary = run.summary()
print(summary["label"])
print(summary["task"])
print(summary["feature_names"])
print(summary["candidate_gates"])
accuracy = run.score(X_test, y_test)
probabilities = run.estimator.predict_proba(X_test)For regression, run.score() returns R-squared. For classification, it returns
accuracy, matching TabPVN.score().
The common application path accepts a plain mapping and returns an
attribute-friendly PredictionResult. It does not apply a reliability gate or
generate proof material unless requested.
customer = {
"tenure_months": 2,
"sessions_30d": 1,
"support_tickets_30d": 5,
"plan": "monthly",
}
result = run.predict_one(
customer,
min_precision=0.80,
explain=True,
)
print(result.prediction) # "churn"
print(result.probability) # 0.892873
print(result.probabilities) # {"stay": 0.107127, "churn": 0.892873}
print(result.meets_threshold) # Trueprediction and the keys in probabilities use the original values from the
training target. Train with labels such as "stay" and "churn" when those are
more useful to consumers than 0 and 1.
result.to_dict() produces a compact transport-ready response:
{
"run": "churn-v1",
"task": "classification",
"prediction": "churn",
"probability": 0.892873,
"probabilities": {"stay": 0.107127, "churn": 0.892873},
"reliability": {
"status": "available",
"threshold": 0.80,
"meets_threshold": True,
"type": "precision_lower_bound",
"value": 0.838109,
},
"explanation": [
{
"conditions": [
{
"feature": "support_tickets_30d",
"operator": "gt",
"value": 3,
"observed": 5,
}
],
"supports": "churn",
}
],
"verification": {
"verified": True,
"audit_reference": "sha256:...",
},
}Without min_precision, meets_threshold is None: no gate was requested.
For regression, use max_error= instead. Explanations are opt-in with
explain=True; the full public receipt is available as result.proof or with
result.to_dict(detail="proof"). An independent audit payload is retained only
with include_artifact=True and returned by result.to_dict(detail="audit").
run.predict() returns a PredictionBatch, not only a NumPy array. Its
frame property preserves a pandas input index and provides analysis-ready
columns.
batch = run.predict(X_test)
display(batch.frame.head())
# Use the same compact result interface for one row from a batch.
first = batch.result(0)
print(first.prediction, first.probability)The frame contains:
| Column | Meaning |
|---|---|
prediction |
Predicted class or regression value |
selected |
Whether the row satisfies the requested reliability threshold |
probability[...] |
One column per fitted class; classification only |
precision_lower_bound |
Regional lower precision bound when available; classification only |
error_bound |
Conformal error bound when available; regression only |
proof_verified |
Proof verification result for rows where a proof was requested |
audit_reference |
Hash binding the public proof to its detailed artifact |
Use precision= for a classification selection threshold or max_error= for a
regression error tolerance:
classification_batch = run.predict(X_test, precision=0.95)
regression_batch = regression_run.predict(X_test, max_error=5.0)Proof generation is opt-in because materializing a proof for every row can be
unnecessary in exploratory work. proofs=True proves every row;
proofs=[0, 7] proves only those zero-based batch positions. These are row
positions, not pandas index labels.
batch = run.predict(
X_test,
proofs=[0, 7],
include_artifacts=True,
)
assert batch.proofs_verified
public_receipt = batch.proof(0)
audit_artifact = batch.artifact(0)
batch.to_jsonl("churn-predictions.jsonl", include_artifacts=True)Every JSONL row includes the run label as run_label, prediction, selection
status, reliability, and probabilities when applicable. Requested public proofs
are included by default. Detailed artifacts are exported only when both the
prediction call and to_jsonl() use include_artifacts=True.
Generating a public proof also verifies it against its audit artifact. When
include_artifacts=False, the batch keeps a private copy only for re-verification;
it cannot be retrieved with artifact() or included in an export.
The run artifact stores the fitted estimator together with its label, creation time, package version, and metadata.
run.save("churn-v1.tabpvn-run")
from tabpvn import TabPVNRun
restored = TabPVNRun.load("churn-v1.tabpvn-run")
assert restored.label == "churn-v1"
display(restored.predict(X_test.iloc[:5]).frame)TabPVNRun.load() has the same trust boundary as TabPVN.load(): run artifacts
contain Python pickle state and must never be loaded from an untrusted source.
For advanced estimator constructor settings, pass an explicit estimator without changing the run workflow:
from tabpvn import TabPVN, fit_run
run = fit_run(
X_train,
y_train,
label="churn-v1",
estimator=TabPVN(task="classification", seed=7, alpha=0.05),
)The public surface follows the familiar sklearn lifecycle. No architecture or dataset-specific parameters are required.
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from tabpvn import TabPVN
dataset = load_breast_cancer(as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(
dataset.data,
dataset.target,
test_size=0.25,
random_state=0,
stratify=dataset.target,
)
model = TabPVN().fit(X_train, y_train)
labels = model.predict(X_test)
probabilities = model.predict_proba(X_test)
accuracy = model.score(X_test, y_test)
print(labels[:5])
print(probabilities[:2])
print(f"accuracy={accuracy:.3f}")row = X_test.iloc[[0]]
# Stable response intended for applications and API clients.
proof = model.proof(row, row=0)
# Detailed derivation intended for auditors and verification services.
artifact = model.proof_artifact(row, row=0)
# Verification needs no fitted model state.
assert TabPVN.check_proof(artifact)
assert TabPVN.check_proof(proof, artifact=artifact)
print(proof["prediction"])
print(proof["reliability"])
print(proof["reasons"])
print(proof["verification"]["audit_reference"])The public response uses typed, programmatic conditions such as:
{
"feature": "mean radius",
"operator": "lte",
"value": 15.2,
"observed": 13.4
}It does not expose tree indexes, logits, internal candidate names, or fitting stages. The separate artifact contains the arithmetic needed for independent verification and is cryptographically bound to the public response by its audit reference.
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from tabpvn import TabPVN
dataset = load_diabetes(as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(
dataset.data,
dataset.target,
test_size=0.25,
random_state=0,
)
model = TabPVN(task="regression").fit(X_train, y_train)
rows = X_test.iloc[:5]
prediction = model.predict(rows)
error_bound = model.confidence(rows)
print(prediction)
if error_bound is not None:
intervals = np.column_stack((prediction - error_bound, prediction + error_bound))
print(intervals)
proof = model.proof(rows, row=0)
artifact = model.proof_artifact(rows, row=0)
assert TabPVN.check_proof(proof, artifact=artifact)Repeated entity-event tables use the same fit method. TabPVN inspects plausible
entity and timestamp roles without labels, evaluates bounded causal representations
on later windows, and keeps the event path only when it improves the ordinary raw
schema.
import numpy as np
import pandas as pd
from tabpvn import TabPVN
event_index = np.arange(60)
amount = np.where(event_index % 5 == 0, 200.0 + event_index, 5.0 + event_index % 10)
train_events = pd.DataFrame(
{
"account_id": np.tile(["a", "b", "c"], 20),
"event_time": pd.date_range("2026-01-01 09:00", periods=60, freq="min"),
"amount": amount,
"country": np.where(event_index % 5 == 0, "US", "EE"),
}
)
labels = (amount > 100.0).astype(int)
model = TabPVN(task="classification").fit(train_events, labels)
future_events = pd.DataFrame(
{
"account_id": ["a", "b"],
"event_time": pd.to_datetime(["2026-01-01 10:12", "2026-01-01 10:13"]),
"amount": [210.0, 7.0],
"country": ["US", "EE"],
}
)
prediction = model.predict(future_events)
print(model.event_schema_) # None when the causal challenger was not admitted.
print(prediction)For unusual column names, entity=, timestamp=, and value_columns= are
available as advanced schema overrides. They are not normal tuning parameters.
Prediction calls never silently mutate the fitted history; separate calls remain
deterministic and concurrency-safe.
model.save("fraud-model.tabpvn")
loaded = TabPVN.load("fraud-model.tabpvn")
prediction = loaded.predict(future_events)Only load artifacts from trusted sources. Model loading reconstructs Python objects and is not a safe boundary for untrusted files.
TabPVN is built around five constraints:
- No pretrained tabular model. Runtime behavior must be implemented in this repository and inspectable.
- No required user tuning. The default estimator chooses bounded candidate schedules and permissions from training-only evidence.
- No validation leakage. Ordinary tables use out-of-fold evidence; event tables use past-to-future validation with equal timestamps kept together.
- No hidden class-changing component. Any component allowed to change a label must carry explicit replayable evidence.
- No universal claim from one benchmark. Accuracy, ranking, calibration, memory, and latency must be reported under the exact task protocol.
The primary research comparison is TabPFN-3. Fixed GBDT implementations remain important secondary baselines because they are strong, fast, and widely deployed. The goal is not to optimize one leaderboard slice; it is to find explicit architectural components that transfer across binary, multiclass, regression, categorical, rare-event, temporal, and large-row tasks.
- Use official OpenML task folds for TabArena comparisons.
- Use ROC AUC for ordinary binary ranking and macro one-vs-one AUC for multiclass.
- Use average precision for rare-event ranking; raw accuracy can hide a model that never finds the minority class.
- Use RMSE or negative RMSE for regression comparisons.
- Report fit time, prediction time, peak memory, package versions, hardware, and the exact Git commit.
- Treat one-fold runs as smoke tests. Promotion claims require repeated or official folds and paired task-level comparisons.
- Keep full Arena and TabPFN-3 runs out of the normal edit loop because they are expensive and slow.
Fast, network-free harness check:
uv run python -m benchmark.experiments.run \
--suite sklearn \
--models tabpvn,hgb \
--splits 3 \
--out results/sklearn-smoke.csvOne bounded TabArena fold with OpenML data:
uv run --extra openml python -m benchmark.experiments.run \
--suite tabarena \
--ta-size 10k-100k \
--models tabpvn,hgb \
--splits 1 \
--reference hgb \
--out results/tabarena-smoke.csvPrimary comparison against TabPFN:
uv run --extra openml --extra pfn python -m benchmark.experiments.run \
--suite tabarena \
--ta-size le10k \
--models tabpvn,tabpfn \
--splits 1 \
--reference tabpfn \
--out results/tabpfn-comparison.csvFor rare binary tasks, add:
--classification-metric average_precision
Omit --splits only when intentionally running every official task fold. Generated
datasets and result files are ignored by Git so benchmark artifacts cannot enter a
source commit accidentally.
core/ trusted first-order-logic verification kernel
tabpvn/ estimator, proposers, proofs, persistence, and tests
tabpvn/proposers/ bounded candidate components and their verifiers
benchmark/ datasets, baselines, protocol runner, and focused audits
scripts/ package release checks
tools/ release-artifact verification
pyproject.toml package metadata and quality configuration
uv.lock reproducible dependency resolution
The runtime wheel contains only core and tabpvn. Benchmark code, tests,
downloaded data, result files, and archived experiments are excluded from release
artifacts.
Fast commit-level checks:
uv run --locked --extra dev ruff check tabpvn core benchmark
uv run --locked --extra dev ruff format --check tabpvn core benchmark
uv run --locked --extra dev mypy
uv run --locked --extra dev pytest -q \
tabpvn/tests/test_production_contract.py \
tabpvn/tests/test_proof_response.py \
tabpvn/tests/test_api.pyComplete package release gate:
./scripts/release_check.shThe release gate runs the full TabPVN test suite and package build. Arena, million-row, and TabPFN-3 evaluations are separate research gates.
TabPVN is research software and the public API is pre-1.0.
- Proof verification establishes that the supplied facts, rules, and arithmetic reproduce the declared model output. It does not observe unknown ground truth.
- Statistical guarantees depend on their calibration and exchangeability or temporal assumptions.
- Held-out promotion reduces the chance of harmful components but cannot guarantee improvement under arbitrary distribution shift.
- Automatic cross-fitting can make
fitmore expensive than a single conventional tree fit. - Full benchmark conclusions must be regenerated from the pinned commit and protocol; result CSVs are not treated as source code.
Until a formal paper is published, cite the repository and pin the commit used in the experiment:
@software{leiro2026tabpvn,
author = {Anthony Leiro},
title = {TabPVN: Proof-Carrying Tabular Prediction},
year = {2026},
url = {https://github.com/lenileiro/TabPVN}
}TabPVN is distributed under the custom TabPVN Research-Only Source License 1.0. It permits use, modification, reproducibility work, benchmarking, and redistribution under the same terms solely for research and evaluation. Commercial use, production use, commercial product development, fee-based services, and operational decisions require a separate written license.
This is a source-available license, not an OSI-approved open-source license.