SolvNum: Examples
Each example is a verified run of a guided tour script from the shipped python/examples/
bundle (see PROGRESSION.md for the full 01→06 sequence). Run the guided tour
first: it narrates the scenario, explains each metric, and shows when to trust the result. Use
the production one-liner when wiring the same API call into your pipeline.
Beta release. Products in this catalog other than the SolvSRK family and SolvScout / SolvTune are beta — suitable for trials and evaluation; APIs and packaging may change before GA. Do not deploy beta builds in production programs without a signed agreement with Resonix. Activate a trial license before running examples — see Install and Licensing.
Journeys
Example 1: Journey 01 — First contact
What SolvNum is and how to prove it is alive. Confirm the native library loaded and report its grid. Examples need a valid license; this shows the current state.
Walkthrough:
-
What you are doing — SolvNum is a deterministic, integer log-domain number type. A value is a positive real 2 ** (q + e / SCALE) stored as two int32 fields, and every operation runs integer-only over frozen tables. Results are bit-for-bit identical across platforms - the basis for attestation.
-
Version and scale — Confirm the native library loaded and report its grid.
version: 0.1.0scale: 16777216scale_bits: 24
-
License — Examples need a valid license; this shows the current state.
license_valid: True
-
Simplest call: round-trip a number — Convert a float into a SolvNum and back. It should reconstruct closely.
input: 9.81solvnum: SolvNum(q=3, e=4936748)reconstructed: 9.809999826205305exact: 9.81rel_error: 1.77e-08— Within the log-domain grid resolution (rel_error <= 1e-06).
Guided tour output (from a verified run):
What SolvNum is and how to prove it is alive.
-- Act 1 - What you are doing --
-> SolvNum is a deterministic, integer log-domain number type. A value is a positive real 2 ** (q + e / SCALE) stored as two int32 fields, and every operation runs integer-only over frozen tables. Results are bit-for-bit identical across platforms - the basis for attestation.
-- Act 2 - Version and scale --
-> Confirm the native library loaded and report its grid.
version: 0.1.0
scale: 16777216
scale_bits: 24
-- Act 3 - License --
-> Examples need a valid license; this shows the current state.
license_valid: True
-- Act 4 - Simplest call: round-trip a number --
-> Convert a float into a SolvNum and back. It should reconstruct closely.
input: 9.81
solvnum: SolvNum(q=3, e=4936748)
reconstructed: 9.809999826205305
exact: 9.81
rel_error: 1.77e-08
-> Within the log-domain grid resolution (rel_error <= 1e-06).
-- Run complete --
version: 0.1.0
round_trip_ok: True
next: python examples/journeys/02_arithmetic.pycd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/01_first_contact.py
# Production one-liner — same API call you ship:
python -c "import solvnum; print(solvnum.version(), solvnum.license_valid())"Example 2: Journey 02 — Arithmetic identities
mul, div, square, add - and why they are reproducible. mul/div/square/add against exact answers; bit-identical determinism.
Walkthrough:
-
Multiplication is addition of exponents — In the log domain, multiplying two numbers adds their exponents - an exact integer add, no rounding. Check 6 * 7 = 42.
reconstructed: 41.99999975269179exact: 42.0rel_error: 5.89e-09— Within the log-domain grid resolution (rel_error <= 1e-06).
-
Division and square — 42 / 6 = 7 and 9^2 = 81, both integer-only.
div_42_by_6: 7.000000024286276square_9: 80.99999696808625
-
Addition lives on the same grid — add() combines two positive values; 3 + 4 = 7 on the log-domain grid.
reconstructed: 6.999999735082739exact: 7.0rel_error: 3.78e-08— Within the log-domain grid resolution (rel_error <= 1e-06).
-
Determinism: same inputs -> same bits — Recompute 6 * 7 and compare the raw (q, e) fields, not just the float.
first: SolvNum(q=5, e=6581994)second: SolvNum(q=5, e=6581994)bit_identical: True— Identical struct fields mean this result is portable across CPUs and compilers.
Guided tour output (from a verified run):
mul, div, square, add - and why they are reproducible.
-- Act 1 - Multiplication is addition of exponents --
-> In the log domain, multiplying two numbers adds their exponents - an exact integer add, no rounding. Check 6 * 7 = 42.
reconstructed: 41.99999975269179
exact: 42.0
rel_error: 5.89e-09
-> Within the log-domain grid resolution (rel_error <= 1e-06).
-- Act 2 - Division and square --
-> 42 / 6 = 7 and 9^2 = 81, both integer-only.
div_42_by_6: 7.000000024286276
square_9: 80.99999696808625
-- Act 3 - Addition lives on the same grid --
-> add() combines two positive values; 3 + 4 = 7 on the log-domain grid.
reconstructed: 6.999999735082739
exact: 7.0
rel_error: 3.78e-08
-> Within the log-domain grid resolution (rel_error <= 1e-06).
-- Act 4 - Determinism: same inputs -> same bits --
-> Recompute 6 * 7 and compare the raw (q, e) fields, not just the float.
first: SolvNum(q=5, e=6581994)
second: SolvNum(q=5, e=6581994)
bit_identical: True
-> Identical struct fields mean this result is portable across CPUs and compilers.
-- Run complete --
checked: mul, div, square, add
bit_identical: True
next: python examples/journeys/03_order_independent_dot.pycd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/02_arithmetic.py
# Production one-liner — same API call you ship:
python -c "import solvnum as s; print(s.to_double(s.mul(s.from_double(6.), s.from_double(7.))))"Example 3: Journey 03 — Order-independent dot
Same terms, any order, identical bits. dot/sum_ are permutation-invariant - bit-identical regardless of order.
Walkthrough:
-
The problem with float summation — In float64, (a + b) + c can differ from a + (c + b) when magnitudes span many orders. That makes bitwise reproducibility impossible. SolvNum reductions are defined to be order-independent by construction.
-
Dot product, two orderings — Same 4 pairs, permuted; compare (q, e).
order_a: SolvNum(q=5, e=-5975104)order_b: SolvNum(q=5, e=-5975104)bit_identical: Truereconstructed: 24.99999755335103exact: 25.0rel_error: 9.79e-08— Within the log-domain grid resolution (rel_error <= 1e-06).
-
sum_ is order-independent too — Shuffle a 12-element list 5 ways; every sum shares the same struct.
distinct_structs: 1all_identical: True— One distinct struct across all permutations -> reductions are deterministic.
Guided tour output (from a verified run):
Same terms, any order, identical bits.
-- Act 1 - The problem with float summation --
-> In float64, (a + b) + c can differ from a + (c + b) when magnitudes span many orders. That makes bitwise reproducibility impossible. SolvNum reductions are defined to be order-independent by construction.
-- Act 2 - Dot product, two orderings --
-> Same 4 pairs, permuted; compare (q, e).
order_a: SolvNum(q=5, e=-5975104)
order_b: SolvNum(q=5, e=-5975104)
bit_identical: True
reconstructed: 24.99999755335103
exact: 25.0
rel_error: 9.79e-08
-> Within the log-domain grid resolution (rel_error <= 1e-06).
-- Act 3 - sum_ is order-independent too --
-> Shuffle a 12-element list 5 ways; every sum shares the same struct.
distinct_structs: 1
all_identical: True
-> One distinct struct across all permutations -> reductions are deterministic.
-- Run complete --
dot_order_independent: True
sum_permutation_invariant: True
next: python examples/journeys/04_attest_hash.pycd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/03_order_independent_dot.py
# Production one-liner — same API call you ship:
python -c "import solvnum as s; a=s.dot([1e3,1e-3,2.,5.5],[1e-3,1e3,.5,4.]); b=s.dot([2.,5.5,1e3,1e-3],[.5,4.,1e-3,1e3]); print((a.q,a.e)==(b.q,b.e))"Example 4: Journey 04 — attest_hash proof
Turn a numerical result into a verifiable digest. pin a result to a SHA-256 digest; re-derive it; tamper-evidence.
Walkthrough:
-
Hash a result vector — Compute a small vector, then hash its SolvNum representation. The digest is 64 hex characters (SHA-256) over the exact (q, e) integers.
attest_hash: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51sha256_chars: 64— Digest reproduced bit-for-bit: the computation is attested and portable.
-
Re-derive it (the verifier's step) — Recompute the identical vector and hash again; the digest must match.
first: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51second: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51matches: True
-
Change one input -> the digest changes — Attestation is sensitive: flip 5.0 to 5.0001 and the digest diverges.
original: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51tampered: b2982325c7b6d2f3739ab246f1d907c500cd4afb016527e36cbecb3386342971differs: Trueattest_hash: b2982325c7b6d2f3739ab246f1d907c500cd4afb016527e36cbecb3386342971sha256_chars: 64— Digest changed: the inputs or their order-normalised values differ.
Guided tour output (from a verified run):
Turn a numerical result into a verifiable digest.
-- Act 1 - Hash a result vector --
-> Compute a small vector, then hash its SolvNum representation. The digest is 64 hex characters (SHA-256) over the exact (q, e) integers.
attest_hash: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51
sha256_chars: 64
-> Digest reproduced bit-for-bit: the computation is attested and portable.
-- Act 2 - Re-derive it (the verifier's step) --
-> Recompute the identical vector and hash again; the digest must match.
first: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51
second: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51
matches: True
-- Act 3 - Change one input -> the digest changes --
-> Attestation is sensitive: flip 5.0 to 5.0001 and the digest diverges.
original: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51
tampered: b2982325c7b6d2f3739ab246f1d907c500cd4afb016527e36cbecb3386342971
differs: True
attest_hash: b2982325c7b6d2f3739ab246f1d907c500cd4afb016527e36cbecb3386342971
sha256_chars: 64
-> Digest changed: the inputs or their order-normalised values differ.
-- Run complete --
digest: 6563fa015a9c4fc8df198d69f9b634d44d386b3cc22c86b7ba221695c8deda51
reproducible: True
tamper_evident: True
next: python examples/journeys/05_attested_energy.pycd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/04_attest_hash.py
# Production one-liner — same API call you ship:
python -c "import solvnum as s; print(s.attest_hash([s.from_double(x) for x in (1.,2.,3.,4.,5.)]))"Example 5: Journey 05 — 05 Attested Energy
Four objects with known mass and speed. We want a kinetic-energy report that any auditor can reproduce bit-for-bit - no float summation order to argue about. KE_i = 1/2 * m_i * v_i^2. square the speed, multiply by mass and 1/2. Total KE via the deterministic reduction.
Walkthrough:
-
The scenario — Four objects with known mass and speed. We want a kinetic-energy report that any auditor can reproduce bit-for-bit - no float summation order to argue about. KE_i = 1/2 * m_i * v_i^2.
-
Per-object kinetic energy — square the speed, multiply by mass and 1/2.
scout_KE_J: 400.0hauler_KE_J: 2457.5998drone_KE_J: 580.8relay_KE_J: 983.1251
-
Fleet total (order-independent sum) — Total KE via the deterministic reduction.
reconstructed: 4421.524591224664exact: 4421.525000000001rel_error: 9.25e-08— Within the log-domain grid resolution (rel_error <= 1e-06).
-
Attest the report — Hash the per-object KE vector; ship the digest with the numbers. A verifier recomputes KE from the same masses/speeds and checks this digest.
attest_hash: 278e31128562893a9d410fce232718f4ba90533ade341107530bce11ef574e53sha256_chars: 64— Digest reproduced bit-for-bit: the computation is attested and portable.
Guided tour output (from a verified run):
Compute physics in SolvNum, then pin it with a digest.
-- Act 1 - The scenario --
-> Four objects with known mass and speed. We want a kinetic-energy report that any auditor can reproduce bit-for-bit - no float summation order to argue about. KE_i = 1/2 * m_i * v_i^2.
-- Act 2 - Per-object kinetic energy --
-> square the speed, multiply by mass and 1/2.
scout_KE_J: 400.0
hauler_KE_J: 2457.5998
drone_KE_J: 580.8
relay_KE_J: 983.1251
-- Act 3 - Fleet total (order-independent sum) --
-> Total KE via the deterministic reduction.
reconstructed: 4421.524591224664
exact: 4421.525000000001
rel_error: 9.25e-08
-> Within the log-domain grid resolution (rel_error <= 1e-06).
-- Act 4 - Attest the report --
-> Hash the per-object KE vector; ship the digest with the numbers. A verifier recomputes KE from the same masses/speeds and checks this digest.
attest_hash: 278e31128562893a9d410fce232718f4ba90533ade341107530bce11ef574e53
sha256_chars: 64
-> Digest reproduced bit-for-bit: the computation is attested and portable.
-- Run complete --
total_kinetic_energy_J: 4421.5246
attest_hash: 278e31128562893a9d410fce232718f4ba90533ade341107530bce11ef574e53
next: python examples/journeys/06_limits.pycd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/05_attested_energy.py
# Production one-liner — same API call you ship:
python examples/journeys/05_attested_energy.pyExample 6: Journey 06 — 06 Limits
What SolvNum will not do, and why that is a feature. Reconstruction is exact to the grid step (scale bits), not to full float64. Tiny rounding is the price of an integer, portable representation. Use SolvNum where reproducibility and attestation matter: positive magnitudes, products, sums of squares, dot products, energy/likelihood accounting. For signed algebra, subtraction to cancellation, or general transcendental work, keep float64 (or SolvSRK for ODE state).
Walkthrough:
-
Positive reals only — A SolvNum is 2 ** (q + e / SCALE), which is strictly positive. Zero and negatives have no representation, so from_double refuses them instead of silently corrupting a result.
from_double(0.0): refused -> solvnum.from_double: nonpositivefrom_double(-1.0): refused -> solvnum.from_double: nonpositive
-
Values live on a discrete grid — Reconstruction is exact to the grid step (scale bits), not to full float64. Tiny rounding is the price of an integer, portable representation.
scale_bits: 24scale: 16777216reconstructed: 0.3333333089093928exact: 0.3333333333333333rel_error: 7.33e-08— Within the log-domain grid resolution (rel_error <= 1e-06).
-
Not a float64 replacement for everything — Use SolvNum where reproducibility and attestation matter: positive magnitudes, products, sums of squares, dot products, energy/likelihood accounting. For signed algebra, subtraction to cancellation, or general transcendental work, keep float64 (or SolvSRK for ODE state).
good_fit: positive products, order-independent reductions, attested digestswrong_tool: signed subtraction, values that cross zero, general float math
Guided tour output (from a verified run):
What SolvNum will not do, and why that is a feature.
-- Act 1 - Positive reals only --
-> A SolvNum is 2 ** (q + e / SCALE), which is strictly positive. Zero and negatives have no representation, so from_double refuses them instead of silently corrupting a result.
from_double(0.0): refused -> solvnum.from_double: nonpositive
from_double(-1.0): refused -> solvnum.from_double: nonpositive
-- Act 2 - Values live on a discrete grid --
-> Reconstruction is exact to the grid step (scale bits), not to full float64. Tiny rounding is the price of an integer, portable representation.
scale_bits: 24
scale: 16777216
reconstructed: 0.3333333089093928
exact: 0.3333333333333333
rel_error: 7.33e-08
-> Within the log-domain grid resolution (rel_error <= 1e-06).
-- Act 3 - Not a float64 replacement for everything --
-> Use SolvNum where reproducibility and attestation matter: positive magnitudes, products, sums of squares, dot products, energy/likelihood accounting. For signed algebra, subtraction to cancellation, or general transcendental work, keep float64 (or SolvSRK for ODE state).
good_fit: positive products, order-independent reductions, attested digests
wrong_tool: signed subtraction, values that cross zero, general float math
-- Run complete --
refuses_nonpositive: True
grid_bounded: True
note: Reproducibility is bought with a narrower domain.
next: (end of suite) - see examples/PROGRESSION.mdcd python
# Guided tour — narrated scenario walkthrough (recommended first run):
python examples/journeys/06_limits.py
# Production one-liner — same API call you ship:
python examples/journeys/06_limits.pyExample bundle
Every journey is a domain scenario with narrated acts — run the guided tour first to see what each number means, then copy the production one-liner into your pipeline.
| Resource | Purpose |
|---|---|
PROGRESSION.md | Ordered runbook — journeys 01→06 with dual commands |
COVERAGE.md | Capability matrix — which APIs each journey exercises |
APPLICATIONS.md | Where the product applies in real programs |
run_examples.py | Interactive menu to launch any journey |
Guided journeys
| # | Script | Scenario |
|---|---|---|
| 01 | 01_first_contact.py | What SolvNum is and how to prove it is alive. |
| 02 | 02_arithmetic.py | mul, div, square, add - and why they are reproducible. |
| 03 | 03_order_independent_dot.py | Same terms, any order, identical bits. |
| 04 | 04_attest_hash.py | Turn a numerical result into a verifiable digest. |
| 05 | 05_attested_energy.py | Four objects with known mass and speed. We want a kinetic-energy report that any auditor c… |
| 06 | 06_limits.py | What SolvNum will not do, and why that is a feature. |
Run from the python/ directory after install and license activation.
Set SOLVNUM_QUIET=1 only when you want silent CLI runs (no narration).