Readmission risk (synthetic)

A classification with its TRIPOD+AI record written by the command.

THE TRAINING DATA IS SYNTHETIC. There is not one real patient in it. All 400 rows were generated by a two-line rule that a person wrote, which ships in this package as data_recipe.txt. A model trained this way is not clinically validated.

THIS IS NOT FOR USE WITH PATIENTS. It is a demonstration of traceability, not a medical device and not a clinical decision aid. See Not for use with patients.

What it is and what it predicts

A 4-input feed-forward classifier (Dense 32 relu β†’ Dense 16 relu β†’ Dense 2 softmax) that maps four made-up admission variables to a probability over two labels, alto (high) and bajo (low).

It exists to show one thing end to end: that a model can travel with the rule its data came from, the digests of every artefact and the environment it was trained in β€” and that from all of that a command can then write a TRIPOD+AI record, including the boxes that record cannot fill.

It is one of the three reproducible cases at matrixaistudio.org/casos. MatrixAI can also build a model from a plain-language description; this case was not made that way, it was made from the .mxai and the recipe published here.

The rule that decided every label, verbatim from data_recipe.txt:

alto: edad > 75 OR reingreso_previo > 0.5
DEFAULT: bajo

That rule is the whole ground truth. Learning it is not a clinical finding.

The rule uses two of the four inputs. dias_ingresado and num_diagnosticos appear in neither clause: they are noise by construction, and no label anywhere depends on them. The .mxai declares an AUDIT / EXPLAIN Input -> Classifier block, but no attribution or explanation is published with this case, so there is nothing here that shows what the network did with those two columns.

The four inputs are continuous scalars, including the ones whose names sound discrete. model.mxai declares all four as Scalar over a range: edad [18, 100], dias_ingresado [0, 60], num_diagnosticos [0, 15], reingreso_previo [0, 1]. The generator samples them as continuous values β€” regenerating the dataset with the command below gives rows such as num_diagnosticos 2.7471 and reingreso_previo 0.3372. That is why the recipe thresholds at > 0.5 rather than testing a boolean, and why the bundle's own example_input.json uses num_diagnosticos 7.5. Read num_diagnosticos as "a number in [0, 15]", not as a count of diagnoses.

Not for use with patients

  • Not a medical device. No regulatory approval of any kind is claimed, implied or available here. Nothing in this repository establishes conformity with any regulation, standard or clinical guideline.
  • Not a clinical decision aid. The labels came from a rule written by one person in two lines. It was not derived from evidence, from a cohort, or from a guideline.
  • No patient data, so no patient validation. There is no cohort, no site, no time period, no external validation and no temporal validation. accuracy below was measured on a validation split of the same synthetic dataset the model trained on.
  • The bundle takes no actions. The exported package's own README states: "Actions remain simulate_only. This bundle only provides predictions."
  • A probability here is not a calibrated risk. Calibration is not computed anywhere in this package, and the TRIPOD+AI record says so explicitly. See What this does NOT prove.

How to reproduce it, end to end

Python 3.10 or newer (requires-python = ">=3.10"), then pip install "matrixai-core[export]". No account, no LLM key, no GPU. Put modelo.mxai, modelo.mxtrain and receta.txt in an empty directory; the commands generate the rest.

matrixai generate-dataset modelo.mxai --training modelo.mxtrain \
  --rows 400 --seed 20260825 --mode coherent --recipe receta.txt -o datos
matrixai train modelo.mxai --training modelo.mxtrain --output runs/v1 \
  --recipe receta.txt --dataset-manifest datos/classifierproject-synthetic-manifest.json
matrixai export-bundle modelo.mxai --params runs/v1/params.best.json \
  --outdir paquete --training modelo.mxtrain --data-recipe receta.txt --from-run runs/v1
zip -r paquete.zip paquete           # or: python -m zipfile -c paquete.zip paquete
matrixai verify paquete.zip --retrain
matrixai report paquete --tripod --locale en -o ficha_tripod.en.md

The zip line is not decoration. matrixai export-bundle writes a directory β€” --outdir is its only output flag and it has no zip option β€” so nothing in the published command list produces a paquete.zip on its own. In this project the zip is made by the build script. Without that line the verify step has no file to open.

--mode coherent samples the inputs inside the ranges the .mxai declares and lets the recipe decide each label (matrixai generate-dataset --mode: "random (uniform) or coherent (model-guided labels)"; --recipe: "the rule that decides the target from the inputs").

Verify the ZIP, not the freshly built directory. Verifying the directory leaves datos/ on disk, and the retrain stage picks the dataset up from there instead of rebuilding it from the recipe β€” which is exactly the step being checked. The same goes for __pycache__: importing predict.py from inside the bundle writes one, and matrixai verify counts it as a file the manifest does not name (all four stages then come back INCOMPARABLE). Use a pristine copy.

What the four stages report, and what you will actually get

What matrixai verify paquete.zip --retrain printed on the machine that built this package (salida.txt, step 5 β€” literal stdout):

manifest  PASS
R1        PASS
training  PASS
R3        PASS

That is not what you will get. Measured on 2026-08-30 against the published paquete.zip with matrixai-core 1.7.0 β€” which is what the pip install line above installs today:

$ matrixai verify paquete.zip --retrain --locale en
manifest  PASS
R1        PASS
training  PASS
R3        INCOMPARABLE  β€” the tolerance was measured for the package's own environment (f73756abfe0d07df…) and this one is different (364849887da933a3…), so a difference here would not prove the package wrong
$ echo $?
3

INCOMPARABLE is one of the four verdicts the tool defines (PASS, FAIL, INCOMPARABLE, NOT_RUN) and it means "I could not check this" β€” a different thing from FAIL. Expect it, and expect exit 3. (The second digest will be different again on your machine; it is a digest of your environment.) Exit codes, from the bundle's own README: 0 nothing failed Β· 2 something does not match Β· 3 it could not be checked.

Pinning the core version will not get you four PASS. environment_sha256 covers the matrixai version, the exact CPython build string, the kernel release and the numpy/onnx/onnxruntime/torch versions (matrixai/export/reproduce.py, build_environment). Measured here: recomputing that digest on this machine with only the version string forced to 1.6.0 reproduces f73756abfe0d07df… exactly β€” so on this machine the core version is the one field that moved, and 1.6.0 is still on PyPI. On any other machine the CPython build string and the kernel release move too, and no pin brings those back. R3's tolerance is scoped same_environment_same_seed; when the environment is not the same, the core says so instead of manufacturing a PASS.

Stage What it checks
manifest Every artefact in the package matches the sha256 the manifest declares, and nothing travels that the manifest does not name.
R1 The dataset rebuilt from the recipe has the full sha256 the package declares.
training Training runs to completion with what the package carries inside.
R3 The metrics of that fresh run fall inside their declared tolerance.

Stage descriptions: https://matrixaistudio.org/manual/proof.

The environment the numbers were measured in

reproduce.json β†’ environment (environment_sha256 f73756abfe0d07dffabba936f5e66218dd56e5bf3ebc8ee0c507ac69bb3f2f15):

matrixai 1.6.0
python 3.12.3 (CPython)
platform Linux 6.8.0-137-generic, x86_64, glibc 2.39
packages numpy 2.4.4 Β· onnx 1.21.0 Β· onnxruntime 1.26.0 Β· torch 2.11.0+cpu
backend / device stdlib / cpu (reproduce.json β†’ generation)
exported at 2026-08-26T15:55:14.631725+00:00 (export_manifest.json β†’ exported_at)

Both published metrics declare tolerance_abs: 0.0 with tolerance_scope: "same_environment_same_seed", so an exact match is only claimed inside that environment with those seeds. If your digests come out different, something changed and it is worth knowing what.

The measured metrics, with their provenance

Both figures come from reproduce.json β†’ metrics, and both carry the digest of the dataset they were measured on β€” 1cefbf74e237efdae2f6e06d7b9c5141ed815cccbd547aa18341ab7a79c96e65.

Metric Value Split Source
accuracy 0.96875 validation reproduce.json β†’ metrics[0]; also salida.txt step 2 (Accuracy: 0.968750)
best_validation_loss 0.07562131317942544 validation reproduce.json β†’ metrics[1]; also salida.txt step 2 (Best validation loss: 0.075621)

What each metric says is missing about itself. reproduce.json gives every metric an incomplete list, and the two lists are not the same:

  • accuracy β†’ evaluator, evaluator_version, tolerance_rel are all null.
  • best_validation_loss β†’ evaluator, evaluator_version, aggregation, tolerance_rel are all null.

0.96875 is 62/64. The package does not record how many rows were used (artifacts.dataset.rows_used is null, and so is sha256_prepared; a null is an answer here, not a zero) β€” but that does not mean it cannot be known, and leaving it at "not recorded" would be half the truth. The 400 generated rows are split into a 320-row train CSV and an 80-row eval CSV (salida.txt step 1). modelo.mxtrain reads only the 320-row train CSV and splits that again train=0.8 / validation=0.2 seed=42. Measured by re-running the published commands and reading runs/v1/training_trace.json: the validation set is 64 rows, and 0.96875 Γ— 64 = 62. So two rows are wrong, and one row is worth 1.56 percentage points.

What 0.96875 is worth against the base rate. Regenerating the dataset with the exact command above β€” the same regeneration whose R1 PASS says it matches the dataset digest the package declares, and which reproduces the published training output exactly (Best epoch: 29, Best validation loss: 0.075621, Accuracy: 0.968750) β€” gives a 320-row training CSV of 205 alto / 115 bajo, a majority-class baseline of 0.6406. In the 64-row validation split the same re-run records 44 alto / 20 bajo, a baseline of 0.6875. So the model lands about 28 points above always answering alto, on a rule that is separable by construction.

Per class, from the same re-run (runs/v1/training_trace.json β†’ validation_metrics; the package publishes none of this, only the global accuracy, whose aggregation is "global"):

predicted alto predicted bajo
true alto (44) 42 2
true bajo (20) 0 20

precision alto 1.0 / bajo 0.909091 Β· recall alto 0.954545 / bajo 1.0 Β· macro-F1 0.964562. Both errors are in the same direction: two high-risk rows called low. The published package contains no confusion matrix, no per-class metric and no class balance β€” those five lines were measured here by re-running it.

Data, seeds and digests

From reproduce.json:

rows generated 400 (artifacts.dataset.rows) β€” salida.txt step 1: 320 train CSV + 80 eval CSV
rows used null β€” not recorded by the package (measured on a re-run: 256 train / 64 validation)
dataset sha256 1cefbf74e237efdae2f6e06d7b9c5141ed815cccbd547aa18341ab7a79c96e65
seeds dataset 20260825 Β· split 42 Β· init 42
generation mode coherent, matrixai.synthetic.v1, recipe format matrixai.recipe.v1, warm_start: false
epochs declared / effective / ran 60 / null / null (measured on a re-run: all 60 ran)
recipe verification verified: true, code regenera_el_dataset
weights source: "trained"
provenance run_capture, schema 1.2, sha256 3dd3c04f819918ef70b96ac86ad28177740ae5eb656bcd44b1e0a0b537971cfa

Two of the three epoch counts are missing, and the package says so. Only the declared 60 is recorded; epochs_effective and epochs_ran are null, and the TRIPOD+AI record prints them as _not available_. The console output of the training step (salida.txt) separately reports Best epoch: 29 and run id efc59afa β€” that is stdout, not something the manifest carries.

Artefact digests

manifest_sha256: 072ffdfca51a37b920cfbf7a90945922d3ddb5ea142501f25e77707e64b49a72 (canonicalisation: json:sort_keys=true,separators=(',',':'),ensure_ascii=true,utf-8). files_covered: 16 β€” every file in the package except reproduce.json itself. A manifest cannot carry its own digest inside; its integrity is declared by manifest_sha256, which is computed by whoever builds the package. salida.txt step 3 lists 17 files in the bundle.

Artefact sha256 matches_capture
model.mxai a164f4380120be24d6cc66ac564bc7af820498cdbcf0caf251398aa904410747 true
model.mxtrain 45235ce15bb7744a43dc7aee32ef1622009be352e2d0926e0d3bd8d7feca18d6 true
data_recipe.txt 90376b2c20ee3bd8ced0409bb7c9bc98116afa02174b118905af3289ab513a37 true
model.onnx 99a730a24c58f1b58f5e98ec03f20f2d1148f8dca9c597272c7ad3b5513567bc (covered by files)
predict.py 4ff7568981a683503c8480cf21f18b7c69f531c57f298d65ce346e60c9080bf1 (covered by files)
inference_spec.json 3bb65450baa1c53ea374b8b1d02fbd7a62e0c068b71cfebd92ffae830d8d8c95 (covered by files)

Model identity (model_manifest.json, inference_spec.json, export_manifest.json): model_hash mxai_9474e16a946fd170, parameter_schema_hash params_1f8b44696ed7d327, parameter_set_id Classifier_initial.

ONNX equivalence

export_manifest.json β†’ equivalence_check: passed: true, max_abs_diff 1.1920928955078125e-07, max_rel_diff 2.8725314183123877e-06, against atol 1e-05 / rtol 0.0001, over n_samples 20 Γ— 2 outputs. salida.txt step 3 prints the same check as Equivalence PASS: max_abs_diff=1.19e-07. Those 20 vectors come from a generator with a fixed default seed of 42 (matrixai/export/equivalence.py) β€” the same 20 points on every export, not a fresh sample β€” and they are already-normalised inputs compared on the model's raw output. Twenty fixed samples is what was checked; it is not a proof of equivalence over the input space.

Using it without MatrixAI

The package predicts from raw, human-readable values with numpy and onnxruntime and nothing else. predict.py applies the same normalisation and label mapping the model was trained with, read from inference_spec.json.

unzip paquete.zip && cd paquete
python -m venv .venv && . .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt                # numpy>=1.24, onnxruntime>=1.16
python predict.py --input example_input.json

That reproduces expected_output.json exactly:

{"alto": 0.01409356389194727, "bajo": 0.9859064817428589}

From your own code:

from predict import MatrixAIModel

model = MatrixAIModel()   # loads inference_spec.json next to predict.py
model.predict({"edad": 92, "dias_ingresado": 10,
               "num_diagnosticos": 3, "reingreso_previo": 0.9})

The field names and the labels are Spanish because they are the contract: input_order is ["edad", "dias_ingresado", "num_diagnosticos", "reingreso_previo"] and the output labels are ["alto", "bajo"]. What happens if you rename one, measured: passing age instead of edad raises MatrixAIModelError: Missing required field 'edad'. β€” no hash is involved. The hash check that does exist in predict.py (_verify_hash) compares the model_hash and parameter_schema_hash in inference_spec.json against the metadata embedded in model.onnx; it says nothing about field names. What editing inference_spec.json would break is its sha256 in reproduce.json, and with it the manifest stage of matrixai verify.

The raw ONNX graph (Input, shape [-1, 4], float32 β†’ Classifier_out3, shape [-1, 2], opset 17) expects an already-normalised vector. Prefer predict.py.

The three published example inputs, and what they contrast

The first two are the files published with the case, and their outputs are the literal stdout recorded in salida.txt step 4; the third ships inside the bundle with its answer in expected_output.json. All three were re-run from the published paquete.zip while writing this card and came out digit for digit identical.

Input file Raw record Output Which clause of the rule fires
entrada.json edad 92, dias_ingresado 10, num_diagnosticos 3, reingreso_previo 0.9 {"alto": 1.0, "bajo": 9.931520189576659e-11} both: edad > 75 and reingreso_previo > 0.5
entrada_joven.json edad 25, dias_ingresado 2, num_diagnosticos 1, reingreso_previo 0.0 {"alto": 0.0002168944920413196, "bajo": 0.9997830986976624} neither
example_input.json (in the bundle) edad 59, dias_ingresado 30, num_diagnosticos 7.5, reingreso_previo 0.5 {"alto": 0.01409356389194727, "bajo": 0.9859064817428589} neither (0.5 is not > 0.5)

So they contrast the two ends of the rule: everything on, and everything off. They do not test the OR separately. No published example has a high edad with no prior readmission, or the reverse, so these files do not show that either clause fires on its own.

"alto": 1.0 is not certainty. bajo is 9.93e-11, and float32 cannot represent 1 βˆ’ 9.93e-11 (its epsilon is about 1.19e-07), so the complement rounds to exactly 1.0. It is a saturated softmax on a synthetic separable rule, not a calibrated probability of anything.

The TRIPOD+AI record: what the command fills, and what it leaves empty

TRIPOD+AI is a 27-item reporting checklist for studies that develop or validate a clinical prediction model (Collins et al., BMJ 2024;385:e078378 β€” an external reference, not something any file in this case states). matrixai report <package> --tripod --locale en writes a record from what the run already captured. The document says of itself, in its own first lines: "Every line comes from what the run recorded; what is not known is said" β€” that is the document's claim about the document, quoted here rather than endorsed. What can be checked, and is checked below, is which boxes came out filled and which came out empty. The full output ships with the case as ficha_tripod.en.md (ficha_tripod.md is the Spanish one). Its first lines also say the data is synthetic and that this is not a medical device.

The record does not travel inside the package. The 17 files in paquete.zip do not include it; it is generated afterwards, from the package, and published alongside the case.

Filled in from the run: data source (coherent), rows, dataset digest, predictors with their declared ranges, split and the three seeds, the data recipe verbatim, the architecture and hyperparameter digests, epochs declared, the environment, both performance metrics with the dataset digest they were measured on, and model availability (ONNX + predict.py).

One caveat on that list: the record's "Rows that trained: 400" is the number of rows generated. Training read the 320-row train CSV and, after the 80/20 split, fitted on 256 of them β€” see the metrics section above.

Printed as _not available_, because the run did not record them: excluded columns; epochs effective; epochs ran.

Named as boxes the record cannot fill at all β€” quoted from ficha_tripod.en.md:

  • Missing data: "the core declares no missing-data policy today, so what was done with them cannot be stated"
  • Calibration: "not computed: the package publishes accuracy and losses, not a calibration curve"
  • Subgroup fairness: "not computed: it would need the subgroups declared and measured one by one"
  • Funding: "belongs to the study's author, not to the run"
  • Conflicts of interest: "idem"
  • Ethical approval and study registration: "idem"
  • Intended use and target population: "a person writes this; this report asks for it, it does not invent it"

Producing a document laid out for a checklist is not the same as meeting the checklist, and this record is not an item-by-item response to the 27 items: it has five sections, and seven of its headings are empty here β€” three because the run neither declares nor computes them (missing data, calibration, subgroup fairness), and four because a person has to write them (funding, conflicts, ethical approval and study registration, intended use and target population).

What this does NOT prove

  • Nothing clinical. The data comes from a two-line rule written by a person: it is there to show the circuit, not to decide about anybody.
  • Not authorship, not authenticity. reproduce.json states its own claim: "This manifest proves the package is internally consistent and reproducible: every artifact needed to rebuild this model travels here with its digest, and each one matches the authoritative capture the core recorded while training it. It does NOT prove authorship or authenticity: signatures are out of scope here." No signature travels with this package.
  • accuracy 0.96875 is not a generalisation claim. It is measured on the validation split of the same generated dataset, produced by the same rule and the same seed the model trained under. There is no data of any other provenance anywhere in this case. It says the network learned a two-line rule; it says nothing about any population.
  • The 80-row eval CSV is generated but no metric on it is published. salida.txt step 1 records Eval: datos/classifierproject-synthetic-eval.csv (80 rows), and both metrics in reproduce.json declare "split": "validation". There is no published number on that eval file.
  • Two of the four inputs do not matter. dias_ingresado and num_diagnosticos are in no clause of the rule, and no attribution is published, so nothing here shows whether the network learned to ignore them.
  • Calibration and subgroup fairness are not computed. Not "acceptable" and not "good" β€” not computed. Treat the softmax outputs accordingly.
  • No missing-data policy is declared, so nothing can be said about how absent values would be handled.
  • R3 is a tolerance check, not an independent evaluation β€” and off the machine that built the package it does not even run to a verdict: it comes back INCOMPARABLE and verify exits
    1. Measured; see the reproduction section above.
  • No regulatory conformity of any kind is claimed or provided by this package, this record, or this repository.
  • salida.txt is stdout only. The build script captures the standard output of each command; anything a command wrote to standard error is not in that transcript. Absence of a warning there is not evidence that no warning was issued.
  • This repository deliberately carries no model-index metrics block. That widget presents a number as a checked result; 0.96875 on a synthetic, separable rule with a 0.6875 base rate is not a result you should read off a badge.

Files in this case

paquete.zip holds the whole bundle β€” the 17 files salida.txt step 3 lists: model.onnx, predict.py, inference_spec.json, model.mxai, model.mxtrain, params.best.json, data_recipe.txt, model_manifest.json, export_manifest.json, reproduce.json, requirements.txt, example_input.json, expected_output.json, README.md, and space/README.md, space/app.py, space/requirements.txt. Sixteen of them carry a sha256 in reproduce.json; the seventeenth is reproduce.json, which cannot hash itself.

Published alongside it: modelo.mxai, modelo.mxtrain, receta.txt, entrada.json, entrada_joven.json, reproduce.json, salida.txt, ficha_tripod.md, ficha_tripod.en.md.

Licence and links

AGPL-3.0-only. The exported predict.py carries SPDX-License-Identifier: AGPL-3.0-only, Β© 2026 Roberto Llamosas Conde.

MatrixAI builds, trains, audits and deploys a neural network from a plain-language description or a CSV, and emits a cryptographic receipt at each step. It is open source under AGPL-3.0, and every exported package predicts with no MatrixAI installed.


Make your own

This model is not a demo to look at β€” it is a case you can redo, and the tool that produced it is free.

MatrixAI Studio turns a written description, or a CSV you already have, into a neural network you can question: it builds it, trains it, and emits a cryptographic receipt at every step, so anybody can re-check what you claim. It runs on your own machine β€” no account, no cloud, no API key.

If something on this page is not true, it should be visible from the outside. That is the whole point of publishing the package and not just the numbers.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support