Product documentation — installation, licensing, and integration guides.
SolvNum
Examples

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:

  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.

  2. Version and scale — Confirm the native library loaded and report its grid.

    • version: 0.1.0
    • scale: 16777216
    • scale_bits: 24
  3. License — Examples need a valid license; this shows the current state.

    • license_valid: True
  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).

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.py
cd 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:

  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).
  2. Division and square — 42 / 6 = 7 and 9^2 = 81, both integer-only.

    • div_42_by_6: 7.000000024286276
    • square_9: 80.99999696808625
  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).
  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.

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.py
cd 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:

  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.

  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).
  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.

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.py
cd 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:

  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.
  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
  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.

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.py
cd 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:

  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.

  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
  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).
  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.

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.py
cd 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.py

Example 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:

  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
  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).
  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

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.md
cd 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.py

Example 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.

ResourcePurpose
PROGRESSION.mdOrdered runbook — journeys 01→06 with dual commands
COVERAGE.mdCapability matrix — which APIs each journey exercises
APPLICATIONS.mdWhere the product applies in real programs
run_examples.pyInteractive menu to launch any journey

Guided journeys

#ScriptScenario
0101_first_contact.pyWhat SolvNum is and how to prove it is alive.
0202_arithmetic.pymul, div, square, add - and why they are reproducible.
0303_order_independent_dot.pySame terms, any order, identical bits.
0404_attest_hash.pyTurn a numerical result into a verifiable digest.
0505_attested_energy.pyFour objects with known mass and speed. We want a kinetic-energy report that any auditor c…
0606_limits.pyWhat 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).