- Celsius to Kelvin
- 1. Check it yourself, before anything else
- 2. Use it without MatrixAI
- 3. Reproduce the whole thing
- 4. Measured numbers, with where each one comes from
- 5. What the model is
- 6. The two numbers it learned β check them by hand
- 7. What this does NOT prove
- 8. Files in this package
- 9. License and links
- Make your own
- 1. Check it yourself, before anything else
Celsius to Kelvin
A regression whose answer you check with a subtraction.
This is a one-input tabular regression: you give it a temperature in degrees
Celsius and it returns the same temperature in kelvin. The point of publishing it
is not that it predicts well β it is that you already know the right answer
(K = C + 273.15), so you can check every number below with a calculator instead
of trusting a metric.
Built with MatrixAI, which turns a written specification into a neural model, trains it, and exports a package that predicts with no MatrixAI installed. 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.
1. Check it yourself, before anything else
Every number in this column comes out of a published file. The right-hand column is a subtraction anybody can do.
| Input | C + 273.15 |
What the package returns | Difference |
|---|---|---|---|
| β40 Β°C | 233.15 | 233.15000236034393 |
2.36eβ06 |
| 0 Β°C | 273.15 | 273.15000146627426 |
1.47eβ06 |
| 50 Β°C | 323.15 | 323.1500059366226 |
5.94eβ06 |
| 100 Β°C | 373.15 | 373.1500029563904 |
2.96eβ06 |
Source: β40, 0 and 100 are the literal step-4 output in
salida.txt; 50 is
expected_output.json in this package (sha256
4b16dcefa0328bd4ce9e5cee18b52b7eb6b40d06d5873a8a38f705594bbc1476), which is the
value predict.py --input example_input.json is supposed to print. The
Difference column is arithmetic done here on those two columns, not a figure
any file publishes.
The residual is float32 rounding, not model error, and you can confirm that too β see Β§6.
Where the four inputs come from. These are simply the inputs the case
publishes: entrada.json (0 Β°C), entrada_100.json and entrada_-40.json at
matrixaistudio.org/casos/kelvin/, plus 50 Β°C, which is the example_input.json
that ships inside the package. There is nothing special about them beyond the fact
that you know the right answer by heart. The model declares
celsius: Scalar[-50, 150] (model.mxai, inference_spec.json), and all four sit
inside that range.
None of the four isolates a single parameter, and it is worth knowing why.
W1 and b1 are not a slope and an offset in degrees: predict.py normalizes the
input to (C + 50) / 200 first, so at 0 Β°C the model does not see 0, it sees 0.25 β
and W1 moves the answer there too. Measured on this package by perturbing W1 by
1 % and recomputing in float32: W1 = 0.8 gives K(0) = 273.15000146627426, while
W1 = 0.808 gives 273.6500024795532 β half a kelvin away at the input where a
slope error is supposed to be invisible. What the four inputs together check is the
whole line, not one coefficient each. (That perturbation was computed here; no
published file contains it.)
How large the residual gets across the range. The largest of the four
deviations above is at 50 Β°C, which is not an extreme β but four points do not
bound anything. Sweeping 201 points across the whole declared range [β50, 150] Β°C
with this package's own predict.py, the largest deviation from C + 273.15 is
1.4638900779573305eβ05 at 69 Β°C and the smallest is 5.96eβ09 at β43 Β°C; at the
two edges it is among the smallest (7.21eβ07 at β50 Β°C, β2.38eβ08 at 150 Β°C). So the
error does not grow towards the edges, and it is about 2.5Γ the largest figure in
the table above. It is float32 rounding noise, not model drift. That sweep was run
here and is not published in any file; it is five lines of Python and you can redo
it.
2. Use it without MatrixAI
The package is self-usable: predict.py + model.onnx + inference_spec.json,
and nothing from MatrixAI. Dependencies are numpy and onnxruntime
(requirements.txt).
python -m venv .venv
. .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python predict.py --input example_input.json
# 323.1500059366226
From your own code:
from predict import MatrixAIModel
model = MatrixAIModel() # loads inference_spec.json next to predict.py
print(model.predict({"celsius": 50.0})) # 323.1500059366226
If you plan to verify the package afterwards, do this on a copy. Importing
predict.pyfrom inside the bundle writes__pycache__/predict.cpython-3XX.pycinto the package directory, andmatrixai verifycounts that as a file the manifest does not name: measured, all four stages then come backINCOMPARABLEwith exit code 3 (manifest INCOMPARABLE β the package ships files the manifest does not cover). Delete it (rm -rf __pycache__) before verifying, or use the package somewhere else. Runningpython predict.py --input β¦as a script does not create it; only importing does. That the check notices at all is what makesmanifest PASSworth anything.
You feed raw human values (50.0, not 0.5): predict.py applies the same
normalization the model was trained with, and puts the answer back on the kelvin
scale.
It clips out-of-range inputs, and you have to ask to be told. The declared
input range is [β50, 150] Β°C. Anything outside is clamped to the edge before the
model sees it β measured by running this package's own predict.py:
{"celsius": 1000} -> 423.14999997615814 (i.e. 150 Β°C clamped, = 150 + 273.15)
{"celsius": -273.15} -> 223.1500007212162 (i.e. -50 Β°C clamped, = -50 + 273.15)
The plain output does not say it clipped. --meta does β this is the literal
output, as the command prints it:
python predict.py --input out_of_range.json --meta
{
"prediction": 423.14999997615814,
"meta": {
"spec_version": 1,
"warnings": [],
"clipped": [
{
"field": "celsius",
"raw_value": 1000,
"normalized_value": 1.0
}
]
}
}
If you wire this into anything, read meta["clipped"]. Those two numbers above
are not in salida.txt; they were measured here by running the shipped
predict.py, and the clipping itself is in predict.py (_encode_scalar).
For the raw ONNX graph β input Reading, shape [-1, 1]; output prediction,
shape [-1] (export_manifest.json for the names and shapes; the float32 dtype
is from model_manifest.json β inputs[0].dtype) β remember it expects an
already normalized vector and returns an un-denormalized value. Prefer
predict.py.
3. Reproduce the whole thing
Nothing here is a "trust the card" step. All four verification stages are
reported below in Β§4 β including the one that will not come back PASS on your
machine, and why that is the correct answer rather than a fault.
3a. Check this package as it stands
pip install "matrixai-core[export]"
matrixai verify . # integrity + rebuild the dataset from the recipe
matrixai verify . --retrain # β¦and train again (slow)
matrixai verify . --json # same report, machine readable
Exit codes (the bundle's own README.md): 0 nothing failed Β· 2 something does
not match Β· 3 it could not be checked. verify also accepts a .zip directly,
and verifying a pristine copy is the right thing to do β see the note in Β§2 and the
third note in Β§3b.
What you will actually get, measured. Run 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. It is not a defect in the
package. The second digest will be different again on your machine: it is a digest
of your environment.
Pinning the core version will not get you four PASS, and it would be dishonest
to suggest it. 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 single field that moved, and 1.6.0 is still installable from PyPI. On any other
machine the CPython build string and the kernel release move as well, 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.
The four PASS lines quoted in Β§4 are the literal transcript of the run that built
this package, on the machine that built it. They are true of that run. They are not
a prediction about yours.
3b. Rebuild it from scratch, from the two source files
model.mxai (the model) and model.mxtrain (the training contract) are in this
repository, and they are text you can read in full. Rename them first:
model.mxtrain names its model as kelvin.mxai in its first line, so the file
has to be called that.
cp model.mxai kelvin.mxai
cp model.mxtrain kelvin.mxtrain
cp data_recipe.txt receta.txt
matrixai generate-dataset kelvin.mxai --training kelvin.mxtrain \
--rows 300 --seed 20260825 --mode coherent --recipe receta.txt -o datos
matrixai train kelvin.mxai --training kelvin.mxtrain --output runs/v1 \
--recipe receta.txt --dataset-manifest datos/celsiustokelvin-synthetic-manifest.json
matrixai export-bundle kelvin.mxai --params runs/v1/params.best.json \
--outdir paquete --training kelvin.mxtrain --data-recipe receta.txt --from-run runs/v1
cd paquete && python3 predict.py --input ../entrada.json
cd .. && zip -r paquete.zip paquete
matrixai verify paquete.zip --retrain
Those are the commands published with the case, plus one that is not: zip.
matrixai export-bundle writes a directory and has no zip option (--outdir is
the only output flag), so the zip has to be made separately β in this project it is
made by the build script, not by the CLI. Three more notes:
entrada.jsonis the case's own example input,{"celsius": 0}β write that one line, or point--inputatexample_input.jsonfrom this package ({"celsius": 50.0}) and expect323.1500059366226.--dataset-manifestis how the training run learns the generation seed, the mode and how many rows existed before the split. Without it the package cannot claim its dataset can be regenerated, and it says so instead of pretending.- Verify the zip, not the directory you just built. A freshly built bundle
directory still has
datos/sitting next to it, and a retrain will quietly use it β which is not the thing you wanted to test.
3c. The recipe is the data
predicted_kelvin = 1*celsius + 273.15
That single line (data_recipe.txt, sha256
7f332dbb7aa59ebf7518dba328d62599febc2e693fd528392dee33c43fe729de) is what the
300 generated rows came from β 240 of which became the training CSV and 60 the eval
CSV (Β§4). It ships inside the package, and verify uses it to regenerate the
dataset and compare the digest β which is what stage R1 is.
3d. What you need
Python 3.10 or newer (requires-python = ">=3.10") and
pip install "matrixai-core[export]". No account, no LLM key, no GPU. It runs on a
laptop.
4. Measured numbers, with where each one comes from
Everything below is either copied from a file in this package or was measured by re-running the published files; each row says which. Nothing is rounded for presentation except where the source itself printed it rounded.
Verification β matrixai verify paquete.zip --retrain
Literal output of the run that built this package, from
salida.txt step 5:
manifest PASS
R1 PASS
training PASS
R3 PASS
For what you will get instead, and why, see Β§3a.
| Stage | What it checks |
|---|---|
manifest |
Every artifact 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. |
Each stage reports one of four verdicts β PASS, FAIL, INCOMPARABLE
("I could not check this"), NOT_RUN β described at
matrixaistudio.org/manual/proof.
Model metrics
| Metric | Value | Split | Dataset sha256 | Source file |
|---|---|---|---|---|
| RΒ² | 1.0 (printed as 1.000000) |
validation | 5827acf2b2b6058d90b6a70d959d882db080bac3ceb6bbe797a801d27201c180 |
reproduce.json β metrics[0]; also salida.txt step 2 |
| MAE | 6.505213034913027e-17 |
validation | same | reproduce.json β metrics[1]; salida.txt prints 6.50521e-17 |
| RMSE | 9.765943671129598e-17 |
β | β | params.best.json β metrics.rmse |
| Validation loss | 9.537365578767625e-33 |
validation | β | params.best.json; salida.txt prints 0.000000 |
Both published metrics declare tolerance_abs: 0.0 and
tolerance_scope: "same_environment_same_seed" β that is, the tolerance is only
claimed for a rerun in the same environment with the same seeds, not for your
machine in general.
Both also carry "incomplete": ["evaluator", "evaluator_version", "tolerance_rel"]
β the package names, itself, the three fields it could not fill.
RΒ² of exactly 1.0 and a MAE of 6.5eβ17 mean the residuals are at the floating-point floor. That is what should happen when the data was generated by a rule the model can represent exactly. See Β§7.
Which rows those metrics are over. The package does not record it
(artifacts.dataset.rows_used is null), and a null is an answer, not a zero β
but it is not a mystery either, and saying only "not recorded" would leave you
believing it cannot be known. The 300 generated rows are split into 240
(β¦-train.csv) + 60 (β¦-eval.csv) by generate-dataset (salida.txt step 1).
model.mxtrain reads only the 240-row train CSV and splits that again
train=0.8 validation=0.2 seed=42 (Β§5). Measured by re-running the published
commands and reading runs/v1/training_trace.json: rows_train: 192,
rows_validation: 48, source: datos/celsiustokelvin-synthetic-train.csv. RΒ² and
MAE are over those 48 rows. The 60-row eval CSV is generated and then never used
by this pipeline; no metric on it is published.
Data
| Rows generated | 300 (reproduce.json β artifacts.dataset.rows) |
| Train / eval split files | 240 rows / 60 rows (salida.txt step 1) |
| Rows the model actually trained on / validated on | 192 / 48 β measured, runs/v1/training_trace.json of a re-run; the package itself does not record it |
| Dataset sha256 | 5827acf2b2b6058d90b6a70d959d882db080bac3ceb6bbe797a801d27201c180 |
| Rows the metric was computed over | null (artifacts.dataset.rows_used) β not recorded in the package |
| Seeds | dataset 20260825, split 42, init 42 |
| Generation mode | coherent |
| Epochs declared | 200 (RUN EPOCHS 200 in model.mxtrain) |
| Epochs actually run | null for both epochs_effective and epochs_ran β not recorded in the package. salida.txt reports Best epoch: 25; measured on a re-run, the trace holds all 200 epochs, so all 200 ran and 25 was the best of them |
| Backend / device | stdlib / cpu |
ONNX equivalence
From export_manifest.json β equivalence_check:
passed: true atol: 1e-05 rtol: 0.0001
max_abs_diff: 3.566741946237073e-08
max_rel_diff: 8.65425281073602e-08
n_samples: 20 n_outputs_per_sample: 1
What that actually compares: 20 already-normalized input vectors drawn from
np.random.default_rng(seed) with seed = 42 by default β the same 20 points on
every export, not a fresh sample β run through the core's own reference runtime and
through onnxruntime, compared on the model's raw output, before predict.py
puts it back on the kelvin scale (matrixai/export/equivalence.py, AGPL, in the
core repo). Scaled up by the Γ250 that denormalization applies, 3.57eβ08 is about
8.9eβ06 K β that conversion is arithmetic done here, not a published figure. It is a
spot check on 20 fixed samples, not a proof of equivalence.
Identity and environment
| Project | CelsiusToKelvin |
| Model hash | mxai_fdbe5973e98a1123 |
| Parameter schema hash | params_e0a02353b5d9884e |
| Parameter set | v1_best |
| Function kind | linear_regression (model_manifest.json) |
| ONNX opset | 17 |
| Exported at | 2026-08-26T15:55:05.170951+00:00 |
| Manifest sha256 | 08f9d835c02574c4d6429063fa4787d08ae6bde432d9de56b3ed9cd97f30f465 |
| Run capture sha256 | 141ef41bb4b29382561fe264dbdcd1d57e8ca70e7a0f1da8eca5432b251e04e8 |
| Environment sha256 | f73756abfe0d07dffabba936f5e66218dd56e5bf3ebc8ee0c507ac69bb3f2f15 |
| Built with | matrixai-core 1.6.0, CPython 3.12.3, Linux 6.8.0-137-generic x86_64 |
| Packages | numpy 2.4.4, onnx 1.21.0, onnxruntime 1.26.0, torch 2.11.0+cpu |
reproduce.json carries a sha256 for 16 of the 17 files that travel, and
reports files_covered: 16, missing: [], conflicts: []. Recomputed here: all
16 match. The one it does not cover is itself β a manifest cannot carry its own
digest inside, so its integrity is declared by manifest_sha256, which is computed
by whoever builds the package. What verify does catch, and why manifest PASS
is worth something, is any extra file the manifest does not name (see the
__pycache__ note in Β§2).
The .mxai, .mxtrain and recipe published at
matrixaistudio.org/casos/kelvin/ are byte-identical to model.mxai,
model.mxtrain and data_recipe.txt here β same three sha256 values, checked
with sha256sum.
5. What the model is
The whole model, from model.mxai:
PROJECT CelsiusToKelvin
VECTOR Reading[1]
celsius: Scalar[-50, 150]
END
PARAM W1 Vector[1]
END
PARAM b1 Scalar
END
FUNCTION PredictedKelvinModel
predicted_kelvin: Scalar = linear(W1 * Reading + b1)
END
GRAPH
Reading -> PredictedKelvinModel
END
One weight and one bias. The training contract (model.mxtrain) trains it with
MSE and SGD at learning rate 0.5, batch 8, split 80/20 with seed 42, for up to
200 epochs, reading datos/celsiustokelvin-synthetic-train.csv.
6. The two numbers it learned β check them by hand
params.best.json holds the entire trained model:
"W1": {"values": [0.8]},
"b1": {"values": 0.0926}
Those look like they have nothing to do with 273.15, and that is only because
they live in normalized space. inference_spec.json gives the two ranges, and
predict.py applies them: input (C β (β50)) / 200, output y Γ 250 + 200.
Substitute:
K = (0.8 Β· (C + 50) / 200 + 0.0926) Β· 250 + 200
= 1.0 Β· (C + 50) + 23.15 + 200
= C + 273.15
In decimal the slope comes out 1 and the offset 273.15 exactly. Stored in float32
they are 0.800000011920929 and 0.09260000288486481, so the trained parameters
are the physical constant to within float32 β and that "to within" is the whole
source of the ~1eβ06 residuals in Β§1. Recomputing the four predictions in float32
from those two numbers alone reproduces all four published outputs digit for digit
β checked here with numpy, and something you can redo in five lines.
That is the strongest statement this case can make, and it needs no metric.
7. What this does NOT prove
Same weight as everything above.
It does not prove the model is good at anything. It converts degrees. It is a control case: it exists so you can check the machinery, not to solve a problem.
RΒ² = 1.0 is not an achievement. The data was generated from
predicted_kelvin = 1*celsius + 273.15, and the model is a single linear unit β
the exact shape of the rule that made the data. A perfect fit is what should
happen. It says nothing about how MatrixAI handles a problem where the answer is
not already inside the model's hypothesis class.
The 48 validation rows came out of the same recipe as the training rows. They are held out from the fitting, but not from the rule: there is no data of any other provenance anywhere in this case. RΒ² = 1.0 says nothing about generalizing beyond that one line.
The data is synthetic. No thermometer was involved, no observation of the physical world, no external dataset. Every row came out of that one line.
It says nothing about generalization, and the range is declared, not enforced. Outside [β50, 150] Β°C the input is clipped to the edge (Β§2), so the package will answer for 1000 Β°C with the answer for 150 Β°C. Nothing here was measured outside the declared range.
Digests are not signatures. reproduce.json states this in its own words:
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.
There is no signed receipt in this package. Anybody can change every file and recompute every digest; what the manifest catches is a package that is internally inconsistent, not one that was rebuilt from scratch by someone else.
training PASS means training completed, not that it was good. And R3 only
compares a fresh run's metrics against a tolerance the package scopes to
same_environment_same_seed. Off the machine that built it, R3 comes back
INCOMPARABLE β measured, see Β§3a β and that is the honest verdict, not a
degraded one.
Some numbers in this package are absent, not zero. params.best.json carries
accuracy: 0.0, macro_f1: 0.0, macro_precision: 0.0, macro_recall: 0.0.
Those are classification fields on a regression model. They are placeholders, not
measurements β do not read them as "0 % accurate". Likewise rows_used,
epochs_effective, epochs_ran, evaluator and evaluator_version are null:
the package does not record them, and does not pretend to.
This is not a conformity assessment of anything. Not a certification, not an audit in any regulatory sense, and not evidence for any regulatory regime in any jurisdiction. It is a package that can be rebuilt and checked, which is a different and much smaller claim.
This repository deliberately carries no model-index metrics block. That
widget presents a number as a checked result, and an RΒ² of 1.0 on data generated
from the model's own hypothesis class is not a result β it is arithmetic.
For the case that does not come out clean, see the third one,
Will it rain tomorrow?: it fits real
observations imperfectly (accuracy 0.762557), and separately it leaves two of the
four verification stages INCOMPARABLE and a third NOT_RUN, because its data
cannot be regenerated from any recipe. Those are two different limits β one of fit,
one of verification β and it has both.
8. Files in this package
| File | What it is |
|---|---|
model.mxai |
The model definition (source of truth) |
model.mxtrain |
Training contract: dataset, split, loss, optimizer, epochs |
params.best.json |
The trained weights β one weight, one bias |
model.onnx |
ONNX model, opset 17 |
model_manifest.json |
Model metadata, hashes, backend contract |
export_manifest.json |
Export metadata, tolerance, equivalence check |
data_recipe.txt |
The rule the training data was generated from |
reproduce.json |
Whether this can be rebuilt, and the digest of every other artifact |
inference_spec.json |
How a raw record maps to the model input |
predict.py |
Standalone wrapper: raw values in, prediction out |
requirements.txt |
numpy + onnxruntime, and nothing else |
example_input.json / expected_output.json |
A runnable example and what it should print |
space/ |
A Hugging Face Space template the exporter emits |
README.md |
The bundle's own readme |
Seventeen files; reproduce.json covers the other sixteen.
9. License and links
AGPL-3.0. The model, the package and the MatrixAI core are all AGPL-3.0-only;
predict.py carries the SPDX header inside the bundle.
- This case, with every artifact to download (
paquete.zip,kelvin.mxai,kelvin.mxtrain,receta.txt, the three example inputs,reproduce.json, andsalida.txtwith the literal output of all five steps): https://matrixaistudio.org/casos#kelvin - The other two cases: Readmission risk (synthetic) Β· Will it rain tomorrow?
- What a receipt and the four verification stages do and do not hold up: 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.
Built with matrixai-core 1.6.0; the current release is 1.7.0. A different
version produces a different environment_sha256 and therefore a different R3
verdict β see Β§3a. That is worth knowing rather than hiding.
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#kelvin
- 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.