{"id":51102642,"url":"https://github.com/bcdev/uncertaintyx","last_synced_at":"2026-06-24T12:01:17.380Z","repository":{"id":351728535,"uuid":"1195949085","full_name":"bcdev/uncertaintyx","owner":"bcdev","description":"Tensor-level uncertainty propagation with JAX","archived":false,"fork":false,"pushed_at":"2026-06-04T15:35:07.000Z","size":396,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T17:16:52.999Z","etag":null,"topics":["algorithmic-differentiation","inverse-problems","jax","remote-sensing","tensors","uncertainty-propagation"],"latest_commit_sha":null,"homepage":"https://bcdev.github.io/uncertaintyx/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bcdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-30T08:14:21.000Z","updated_at":"2026-06-04T15:35:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"166e2d17-d322-4ec7-9be6-ea711c3ed0e2","html_url":"https://github.com/bcdev/uncertaintyx","commit_stats":null,"previous_names":["bcdev/uncertaintyx"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bcdev/uncertaintyx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcdev%2Funcertaintyx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcdev%2Funcertaintyx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcdev%2Funcertaintyx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcdev%2Funcertaintyx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bcdev","download_url":"https://codeload.github.com/bcdev/uncertaintyx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcdev%2Funcertaintyx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34731256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithmic-differentiation","inverse-problems","jax","remote-sensing","tensors","uncertainty-propagation"],"created_at":"2026-06-24T12:01:16.313Z","updated_at":"2026-06-24T12:01:17.362Z","avatar_url":"https://github.com/bcdev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Bernstein basis](https://github.com/user-attachments/assets/80d2ee7c-3e1f-4242-ac07-6880516eaf8e \"Bernstein basis\")\n\n# Context\n\nIn an algorithm-driven measurement context, the “measurement models” are\nnot static laboratory instruments but complex, evolving data-processing\ncodes. In this setting, the classical [GUM](https://doi.org/10.59161/JCGMGUM-1-2023)\nframework—built on assumptions of a fixed analytical model, a fixed data\nflow, and manually maintained analytical Jacobians—offers less immediate\npractical guidance. The true forward map is defined by the current state of\nthe code, which evolves as algorithms, implementations, and data dependencies\nchange.\n\nAlgorithmic differentiation (AD) provides a more flexible foundation: it\nderives local linearizations directly from the implementation whenever\nneeded, ensuring that sensitivity information remains consistent with\nthe code itself. Combined with random sampling methods for strongly \nnonlinear behaviour, AD enables uncertainty propagation to be formulated\nin terms of algorithmically differentiable programs.\n\nAs the computational backbone of much of modern machine learning, AD\nframeworks represent inputs, outputs, and sensitivities as dynamic\ntensor-valued objects, freeing the uncertainty calculus from reliance on\nfixed closed-form formulas. Moreover, AD allows sensitivities and\nuncertainties to be integrated **within** data‑processing codes themselves,\nturning them from external annotations into active elements of\ncomputational workflows.\n\n\u003e **Note**\n\u003e [NPL Report CMSC 26/03](https://eprintspublications.npl.co.uk/2828/1/cmsc26.pdf)\n\u003e ranks AD as the most accurate. AD workflows were instrumental\n\u003e within the [FIDUCEO](https://www.fiduceo.eu)\n\u003e project to enable the multi‑mission remote sensing calibration\n\u003e that underpins Earth climate data records\n\u003e ([Quast et al., 2019](https://doi.org/10.3390/rs11050480);\n\u003e [Giering et al., 2019](https://doi.org/10.3390/rs11091002)).\n\n# Synopsis\n\n**Uncertaintyx** (or just **Tyx**) is a lightweight framework for\ntensor‑level uncertainty propagation, inverse problems, and\nmetrology‑aware workflows. It produces uncertainty tensors by combining\ntensor‑valued models with AD backends such as [JAX](https://docs.jax.dev/).\nConventional [NumPy](https://numpy.org)\nacts as a bidirectional interoperability layer, enabling JAX‑based code\nto interoperate smoothly with existing workflows.\n\n**Why tensors?** Remote sensing imagery provides 2D data, spectral imagers\ndeliver 3D data, and Earth climate records form 4D datasets—with ocean\nand atmosphere data reaching up to 5D. Applying standard matrix-based\nuncertainty propagation requires flattening these N-D arrays into 1D\nvectors, which obscures the vital spatiotemporal structure of both the\ndata and the algorithms designed to analyse it. Tensors are the ideal\nsolution, and the law of propagation of uncertainty, when formulated\nand coded in general tensor form, is elegantly beautiful. If you’re curious,\ncompare [NIST TN 1297](https://www.nist.gov/pml/nist-technical-note-1297/nist-tn-1297-appendix-law-propagation-uncertainty) (Equation A-3)\nwith the tensor equation and code further below.\n\n**Why JAX?** Traditional methods like finite differences, manual Jacobians,\nor Monte Carlo often struggle with scalability for high-dimensional\ntensors, demanding extensive evaluations or approximations that compromise\nfidelity. Frameworks like JAX, facilitating GPUs and TPUs besides CPUs,\nmake algorithmic differentiation a Game Changer, automatically generating\nexact Jacobians, Hessians and higher order derivatives—even for complex,\nnonlinear models.\n\n**How does it work?** You define and code a function that maps from one\ntensor space to another:\n\n$$\nf: \\mathbb{R}^{m_1 \\times \\cdots \\times m_{N_m}} \\to \n\\mathbb{R}^{n_1 \\times \\cdots \\times n_{N_n}}, \\quad\nf(x) \\mapsto y\n$$\n\nHere, $x$ and $y$ may be scalars, vectors, matrices, or higher-order\ntensors of arbitrary shape. The function may also depend on parameters \n$p$, which themselves can be tensors of arbitrary shape:\n\n$$\nf: \\mathbb{R}^{k_1 \\times \\cdots \\times k_{N_k}} \\times \n\\mathbb{R}^{m_1 \\times \\cdots \\times m_{N_m}} \\to \n\\mathbb{R}^{n_1 \\times \\cdots \\times n_{N_n}}, \\quad\nf(p, x) \\mapsto y\n$$\n\n**Tyx** extends this formulation by introducing a batch dimension\n$M \\in \\mathbb{N}$ into the function signature:\n\n$$\nf: \\mathbb{R}^{k_1 \\times \\cdots \\times k_{N_k}} \\times \n\\mathbb{R}^{M \\times m_1 \\times \\cdots \\times m_{N_m}} \\to \n\\mathbb{R}^{M \\times n_1 \\times \\cdots \\times n_{N_n}}, \\quad\nf(p, X) \\mapsto Y\n$$\n\nThe main objective of Tyx is to provide efficient access to\nuncertainty tensors for such functions. While Jacobians themselves\nare obtained through automatic differentiation, Tyx delivers a\nhigh-level interface, utilities, and structured handling for them.\nThese Jacobians form the foundation for parameter estimation,\nsensitivity analysis, and uncertainty propagation within the\nframework.\n\nThe **Single-Input Tensor Paradigm** is lightweight and modern,\nfollowing the design principles of leading machine learning frameworks.\nBy accepting a single input tensor of arbitrary shape, the model\nremains both flexible and conceptually clean—supporting multiple\nlogical inputs without cluttering the function signature. Organizing\nand assembling these logical inputs into a unified tensor structure is\nthe user’s responsibility. In this role, you serve as the *Thalamus*—the\ninterface channelling structured data into the computational core\nof Tyx.\n\n\u003e **Note**\n\u003e The batch dimension $M$ enumerates independent samples (e.g.,\n\u003e sensor scans, simulations, ensemble members) but you get to define\n\u003e what “one sample” is: a single pixel value, a spectrum, a scan line,\n\u003e or a spatiotemporal cubelet. Tyx treats that single sample as\n\u003e a tensor $x$, and the framework scales it to a batch $X$ of $M$ such\n\u003e samples. Many remote‑sensing workflows implicitly assume “one sample\n\u003e is one pixel”, but this is often an oversimplification that obscures\n\u003e the full structure of the data and its uncertainties.\n\n# Law of propagation of uncertainty\n\nUsing Einstein's summation convention and the symmetry of the\ninput uncertainty tensor $U$, the law of propagation of uncertainty\nin general tensor form reads:\n\n$$V_{\\dots ij} = G_{\\dots ik} U_{\\dots lk} G_{\\dots jl},$$\n\nwith multi-indices $k, l \\in D \\subset \\mathbb{N}^d$ for some\n$d \\in \\mathbb{N}$. The summation is taken over all $k, l \\in D$.\nHere, $D$ denotes the set of inner tensor indices (multi-indices\nof length $d$), and the trailing tensor dimensions of the Jacobian\ntensor $G$ and the input uncertainty tensor $U$ correspond to\nthese indices. The code below provides an implementation. \n\n```python\ndef make_lpu(d: int) -\u003e Callable[[Array, Array], Array]:\n    \"\"\"\n    Returns the law of propagation of uncertainty.\n\n    :param d: The number of inner tensor dimensions.\n    :returns: The law of propagation of uncertainty.\n    \"\"\"\n\n    @jax.jit\n    def lpu(g: Array, u: Array) -\u003e Array:\n        r\"\"\"\n        The law of propagation of uncertainty.\n\n        :param g: The Jacobian tensor :math:`G`.\n        :param u: The uncertainty tensor :math:`U`.\n        :returns: The uncertainty tensor :math:`V`.\n        \"\"\"\n        dims = tuple(range(-d, 0))\n        return jnp.tensordot(jnp.tensordot(g, u, (dims, dims)), g, (dims, dims))\n\n    return lpu\n```\n\nTyx hereby acts as a modern bridge, translating the rigorous logic\nof the Law of Propagation of Uncertainty into the high-dimensional,\ntensor-valued language of today’s computational frameworks.\n\n\u003e **Note**\n\u003e Tyx passes the GUM example cases with explicit measurement\n\u003e models in [JCGM 102:2011](https://doi.org/10.59161/JCGM102-2011)\n\u003e (Examples 9.2, 9.3, and 9.4) which are implemented as unit‑level\n\u003e tests to verify correctness and accuracy to the last digit\n\u003e listed.\n\n[![CodeQL Advanced](https://github.com/bcdev/uncertaintyx/actions/workflows/codeql.yml/badge.svg)](https://github.com/bcdev/uncertaintyx/actions/workflows/codeql.yml)\n[![Python package](https://github.com/bcdev/uncertaintyx/actions/workflows/python-package.yml/badge.svg)](https://github.com/bcdev/uncertaintyx/actions/workflows/python-package.yml)\n[![codecov](https://codecov.io/gh/bcdev/uncertaintyx/graph/badge.svg?token=742AWtYDCD)](https://codecov.io/gh/bcdev/uncertaintyx)\n![loc](https://img.shields.io/badge/loc-2.3k-blue)\n\n\u003cscript\u003e\nMathJax = {\n  tex: {\n    inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n  }\n};\n\u003c/script\u003e\n\u003cscript id=\"MathJax-script\" async\n  src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js\"\u003e\n\u003c/script\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcdev%2Funcertaintyx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbcdev%2Funcertaintyx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcdev%2Funcertaintyx/lists"}