- Readmission risk (synthetic)
- What it is and what it predicts
- Not for use with patients
- How to reproduce it, end to end
- The measured metrics, with their provenance
- Using it without MatrixAI
- The TRIPOD+AI record: what the command fills, and what it leaves empty
- What this does NOT prove
- Files in this case
- Licence and links
- Make your own
- What it is and what it predicts
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.
accuracybelow 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_relare allnull.best_validation_lossβevaluator,evaluator_version,aggregation,tolerance_relare allnull.
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.jsonstates 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.96875is not a generalisation claim. It is measured on thevalidationsplit 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.txtstep 1 recordsEval: datos/classifierproject-synthetic-eval.csv (80 rows), and both metrics inreproduce.jsondeclare"split": "validation". There is no published number on that eval file. - Two of the four inputs do not matter.
dias_ingresadoandnum_diagnosticosare 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.
R3is 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 backINCOMPARABLEandverifyexits- 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.txtis 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-indexmetrics block. That widget presents a number as a checked result;0.96875on 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.
- This case, with every artefact to download: https://matrixaistudio.org/casos#clinico
- The other two cases: Celsius to Kelvin Β· Will it rain tomorrow?
- What a receipt holds up, and what it does not: https://matrixaistudio.org/manual/proof
- Site: https://matrixaistudio.org
- Source (AGPL-3.0): https://github.com/robertollweb/matrixAI
- PyPI: https://pypi.org/project/matrixai-core/ β
pip install "matrixai-core[export]"
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.
- Download it and build your own: https://matrixaistudio.org/
- Try it in the browser first, installing nothing: https://matrixaistudio.org/demo/
- This case, with every file and command: https://matrixaistudio.org/casos#clinico
- What it does β and what it does not: https://matrixaistudio.org/what-you-get
- How the receipts work: https://matrixaistudio.org/manual/proof
- Source (AGPL-3.0): https://github.com/robertollweb/matrixAI Β· PyPI:
pip install matrixai-core
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.