Skip to main content

Unit-aware arithmetic with Monte Carlo uncertainty propagation

Project description

quantia

A pure-Python library for unit-aware arithmetic with first-class support for Monte Carlo uncertainty propagation.

No numpy required. No dependencies beyond the standard library.


Installation

pip install quantia

The Four Types

Type Use when
UnitFloat exact scalar with a unit
UnitArray exact vector with a unit
ProbUnitFloat uncertain scalar (Monte Carlo samples)
ProbUnitArray uncertain vector of the same quantity

Quick Start

Exact scalars

import quantia as qu

d = qu.Q(100.0, "m")
t = qu.Q(9.81,  "s")
v = d / t                        # UnitFloat(10.19…, 'm/s')

v.to("km/h")                     # unit conversion
v.to_si()                        # always works

qu.Q(100.0, "°C").to("K")        # UnitFloat(373.15, 'K')
qu.Q(100.0, "°C").to("°F")       # UnitFloat(212.0, '°F')

Exact arrays

heights = qu.QA([1.75, 1.80, 1.65], "m")

heights.mean()                   # UnitFloat(1.733…, 'm')
heights.sum()
heights.to("cm")

# Boolean mask filtering
tall = heights[heights > qu.Q(1.78, "m")]   # UnitArray([1.80], 'm')

Uncertainty propagation

with qu.config(n_samples=2000, seed=42):
    efficiency = qu.ProbUnitFloat.uniform(0.88, 0.95, "1")
    power_in   = qu.ProbUnitFloat.normal(500.0, 10.0, "W")

power_out = efficiency * power_in

power_out.mean()                 # UnitFloat(~457 W)
power_out.std()
power_out.interval(0.95)         # (lo, hi) 95% CI as UnitFloat tuple
power_out.percentile(10)

Correlated inputs

When inputs are not independent, use a Gaussian copula:

src = qu.CorrelatedSource(n_vars=2, rho=0.8)

x = src.draw(0, "normal",  "m",  mean=10,   std=1)
y = src.draw(1, "uniform", "s",  low=1,     high=3)

speed = x / y                    # ProbUnitFloat, correlated samples

Or pass a full correlation matrix:

src = qu.CorrelatedSource(corr_matrix=[
    [1.0, 0.7, 0.4],
    [0.7, 1.0, 0.3],
    [0.4, 0.3, 1.0],
])

Unit Expressions

quantia parses unit strings with a full tokenizer:

qu.Q(9.81,  "m/s^2")
qu.Q(1.0,   "kg·m/s^2")          # · or * for multiplication
qu.Q(4.0,   "m^(1/2)")           # rational exponents
qu.Q(1.0,   "kg/m^2/s")          # chained division

Built-in unit domains

Domain Examples
SI base m, kg, s, K, A, mol
SI derived N, J, W, Pa, V, Ω, Hz
Temperature °C, °F, K
Imperial ft, lb, psi, BTU, hp, mph
Petroleum bbl, Mbbl, psi_g, scf, Mscf, °API
Data B, KB, MB, GB, TB, bit

Semantic / tagged units

Dimensionally equal but semantically distinct units that won't cancel:

from quantia import register_tagged

register_tagged("Sm3_res", "m3", "reservoir")
register_tagged("Sm3_st",  "m3", "stock_tank")

Rs = qu.Q(150.0, "Sm3_res") / qu.Q(1.0, "Sm3_st")
# UnitFloat(150.0, 'Sm3_res/Sm3_st')  — does not reduce to 1

Math Functions

quantia.math is a drop-in replacement for the stdlib math module that dispatches transparently on all four types:

import quantia.math as mmath

mmath.log10(x)    # float, UnitFloat, ProbUnitFloat, UnitArray, ProbUnitArray
mmath.exp(x)
mmath.sqrt(x)     # preserves units: sqrt(m^2) → m
mmath.sin(x)      # requires angle unit on UnitFloat; raises DimensionError otherwise
mmath.cos(x)
mmath.atan2(y, x)

Configuration

with qu.config(n_samples=5000, seed=42):
    x = qu.ProbUnitFloat.normal(10.0, 1.0, "m")
    y = qu.ProbUnitFloat.uniform(0.0, 1.0, "s")
# config restored on exit; contexts nest cleanly

Serialization

# Save and load any quantia object
qu.save(power_out, "result.json")
result = qu.load("result.json")

# Dict round-trip
d   = power_out.to_dict()
pf2 = qu.from_dict(d)

CSV export

# UnitArray — single column of values
heights.to_csv("heights.csv")

# ProbUnitArray — mean, std, CI bounds per element
layer_thicknesses = qu.QPA([t1, t2, t3])   # must be same unit
layer_thicknesses.to_csv("stats.csv", confidence=0.90)
layer_thicknesses.samples_to_csv("raw.csv")

Error Handling

quantia raises named exceptions from quantia._exceptions:

from quantia import IncompatibleUnitsError, DimensionError, UnknownUnitError

qu.Q(1.0, "m") + qu.Q(1.0, "s")   # → IncompatibleUnitsError
mmath.sin(qu.Q(1.0, "m"))          # → DimensionError
qu.Q(1.0, "furlongs")              # → UnknownUnitError

Running Tests

pip install pytest
pytest tests/ -v

56 tests covering scalar arithmetic, array operations, probabilistic math, unit algebra invariants, serialization, and the config system.


Benchmarks

python -m quantia.profiling.benchmark
python -m quantia.profiling.benchmark --profile   # cProfile detail at n=10k
python -m quantia.profiling.benchmark --n 10000   # single sample size

Typical throughput on a modern laptop (stdlib backend, no numpy):

Operation n=10k
Scalar arithmetic chain ~1ms
ProbUnitFloat.normal construction ~4ms
mean() ~0.1ms
interval(0.95) ~0ms (cached sort)
Gaussian copula k=3 ~33ms

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quantia-0.1.0.tar.gz (34.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quantia-0.1.0-py3-none-any.whl (40.3 kB view details)

Uploaded Python 3

File details

Details for the file quantia-0.1.0.tar.gz.

File metadata

  • Download URL: quantia-0.1.0.tar.gz
  • Upload date:
  • Size: 34.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for quantia-0.1.0.tar.gz
Algorithm Hash digest
SHA256 99ba181f362815ebec0c669ab44710a5ed91a976c8b806bc05dbf0c9db74ed1e
MD5 ac940648ce284a527e78efa17c76dcfc
BLAKE2b-256 4c47106e7d80e5347ecd82baefe03879a0dedc99b4f83650db70c726f85f11b3

See more details on using hashes here.

File details

Details for the file quantia-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: quantia-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 40.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for quantia-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 023a34921bca72851c22325252aa596d87afcc8ecd2887c0cefe78b964284606
MD5 c2c710f42d74e42cfc26300a2ef95f77
BLAKE2b-256 90f94f2cfaa0b83b10a508c57a308832f99202fa9f2af8f037b1bcdc8fb63d9d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page