torchbelief 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- torchbelief-0.2.0/LICENSE +21 -0
- torchbelief-0.2.0/PKG-INFO +306 -0
- torchbelief-0.2.0/README.md +280 -0
- torchbelief-0.2.0/pyproject.toml +37 -0
- torchbelief-0.2.0/setup.cfg +4 -0
- torchbelief-0.2.0/tests/test_capacity.py +122 -0
- torchbelief-0.2.0/tests/test_core.py +139 -0
- torchbelief-0.2.0/tests/test_extensions.py +294 -0
- torchbelief-0.2.0/torchbelief/__init__.py +22 -0
- torchbelief-0.2.0/torchbelief/belnap.py +70 -0
- torchbelief-0.2.0/torchbelief/capacity.py +176 -0
- torchbelief-0.2.0/torchbelief/choice.py +66 -0
- torchbelief-0.2.0/torchbelief/combine.py +244 -0
- torchbelief-0.2.0/torchbelief/continuous.py +111 -0
- torchbelief-0.2.0/torchbelief/decision.py +99 -0
- torchbelief-0.2.0/torchbelief/frame.py +55 -0
- torchbelief-0.2.0/torchbelief/inconsistency.py +170 -0
- torchbelief-0.2.0/torchbelief/layers.py +127 -0
- torchbelief-0.2.0/torchbelief/mass.py +72 -0
- torchbelief-0.2.0/torchbelief/possibility.py +55 -0
- torchbelief-0.2.0/torchbelief/sparse.py +164 -0
- torchbelief-0.2.0/torchbelief/transforms.py +82 -0
- torchbelief-0.2.0/torchbelief.egg-info/PKG-INFO +306 -0
- torchbelief-0.2.0/torchbelief.egg-info/SOURCES.txt +25 -0
- torchbelief-0.2.0/torchbelief.egg-info/dependency_links.txt +1 -0
- torchbelief-0.2.0/torchbelief.egg-info/requires.txt +4 -0
- torchbelief-0.2.0/torchbelief.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Antonin Sulc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: torchbelief
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Differentiable belief functions (Dempster-Shafer, Yager, TBM, Dubois-Prade, ...) and decision rules in PyTorch
|
|
5
|
+
Author: Antonin Sulc
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sulcantonin/torchbelief
|
|
8
|
+
Project-URL: Documentation, https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md
|
|
9
|
+
Project-URL: Changelog, https://github.com/sulcantonin/torchbelief/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/sulcantonin/torchbelief/issues
|
|
11
|
+
Keywords: belief functions,Dempster-Shafer,evidential,uncertainty,pytorch,imprecise probability,sensor fusion
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: torch>=2.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<picture>
|
|
29
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/logo/wordmark-dark.svg">
|
|
30
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/logo/wordmark-light.svg" alt="torchbelief" width="420">
|
|
31
|
+
</picture>
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
<p align="center"><b>Differentiable belief functions and imprecise probabilities for PyTorch.</b><br>
|
|
35
|
+
Fuse sources that disagree, find out which one is wrong, and decide, or abstain, when the evidence doesn't single out an answer.</p>
|
|
36
|
+
|
|
37
|
+
<p align="center"><b>New to belief functions? Start with the <a href="https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md">visual guide</a></b>: ten pictures, no theory required.</p>
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<picture>
|
|
42
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/pipeline-dark.svg">
|
|
43
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/pipeline-light.svg" alt="Five steps: describe the evidence, convert it to a Capacity, check for disagreement, fuse the sources, decide or abstain.">
|
|
44
|
+
</picture>
|
|
45
|
+
|
|
46
|
+
A probability gives one number per event. `torchbelief` keeps **two**, a lower and an upper bound
|
|
47
|
+
`[Bel(A), Pl(A)]`, and carries them through fusion, conflict handling and decision making. Every
|
|
48
|
+
operation is a batched PyTorch op, so the whole pipeline is differentiable and can sit inside a network.
|
|
49
|
+
|
|
50
|
+
The logo shows the same idea: the solid inner flame is **belief**, the outlined outer flame is
|
|
51
|
+
**plausibility**, and the gap between them is the imprecision the library tracks.
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install torchbelief # needs Python ≥ 3.9 and torch ≥ 2.0
|
|
57
|
+
pip install -e ".[dev]" # from a clone, + pytest
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quickstart
|
|
61
|
+
|
|
62
|
+
Four sensors classify an object; one of them is broken.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import torch, torchbelief as tb
|
|
66
|
+
from torchbelief import inconsistency as I, choice
|
|
67
|
+
|
|
68
|
+
F = tb.Frame(3, names=["car", "truck", "bike"])
|
|
69
|
+
lidar = tb.Capacity.from_mass(tb.simple_support(F, F.subset(["car"]), 0.7), F) # belief function
|
|
70
|
+
camera = tb.Capacity.from_probability(torch.tensor([0.75, 0.20, 0.05]), F) # probability
|
|
71
|
+
radar = tb.Capacity.from_possibility(torch.tensor([1.0, 0.6, 0.1]), F) # possibility
|
|
72
|
+
broken = tb.Capacity.from_mass(tb.simple_support(F, F.subset(["bike"]), 0.9), F) # faulty sensor
|
|
73
|
+
sources = [lidar, camera, radar, broken]
|
|
74
|
+
|
|
75
|
+
I.shapley_blame(sources) # tensor([0.050, 0.108, 0.083, 0.608]) ← the broken one
|
|
76
|
+
fixed, alpha = I.blame_discount(sources) # discount each source by its blame
|
|
77
|
+
m, conflict = tb.combine([s.mass() for s in fixed], F, rule="pcr6")
|
|
78
|
+
fused = tb.Capacity.from_mass(m, F)
|
|
79
|
+
|
|
80
|
+
fused.pignistic() # tensor([0.790, 0.103, 0.107])
|
|
81
|
+
fused.expectation_bounds(torch.eye(3)) # lower [0.73, 0.04, 0.08], upper [0.88, 0.19, 0.17]
|
|
82
|
+
choice.decide_or_abstain(fused, torch.eye(3)) # tensor(0) → "car" (−1 would mean abstain)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### On real video
|
|
86
|
+
|
|
87
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/tracking-demo.webp" alt="YOLO11n with ByteTrack on a rainy street. For each track a side panel shows YOLO's label this frame and torchbelief's label with its belief interval per class; where YOLO flips between car and truck, torchbelief shows car, or car or truck.">
|
|
88
|
+
|
|
89
|
+
YOLO with ByteTrack on real traffic. YOLO relabels every frame, so a hatchback flickers between
|
|
90
|
+
"car" and "truck". A belief function per track ([`examples/tracking.py`](https://github.com/sulcantonin/torchbelief/blob/main/examples/tracking.py),
|
|
91
|
+
two lines per frame) says "car or truck" while the evidence is split. Across the clip, YOLO
|
|
92
|
+
changes label 74 times and torchbelief 20 times. The [visual guide](https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md#9-real-video-labels-that-dont-flicker)
|
|
93
|
+
explains how.
|
|
94
|
+
|
|
95
|
+
### With a team of LLM agents
|
|
96
|
+
|
|
97
|
+
Five agents look for the root cause of an outage, each with one slice of the evidence: logs,
|
|
98
|
+
metrics, the deploy diff, network flows, past incidents. [`examples/multi_agent.py`](https://github.com/sulcantonin/torchbelief/blob/main/examples/multi_agent.py)
|
|
99
|
+
turns each reply into a belief function over the causes `deploy, database, network, dns, cache`:
|
|
100
|
+
|
|
101
|
+
- **API models** (`--live`, Claude) name a *set* of plausible causes and a confidence. The set is a
|
|
102
|
+
focal set, so "not the network" and "can't tell" need no special handling.
|
|
103
|
+
- **Local models** (`--local`, any Hugging Face causal LM) are not asked for a confidence. Each
|
|
104
|
+
cause name is scored by its token log-probability after the prompt. The probability the model
|
|
105
|
+
puts on *none* of the names is treated as ignorance, so an agent that would rather answer
|
|
106
|
+
something else is discounted automatically.
|
|
107
|
+
|
|
108
|
+
With Qwen2.5-1.5B-Instruct as every agent (`--temperature 5`, replies saved in
|
|
109
|
+
[`docs/data`](https://github.com/sulcantonin/torchbelief/blob/main/docs/data/multi-agent-qwen2.5-1.5b.json)):
|
|
110
|
+
|
|
111
|
+
<picture>
|
|
112
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/agents-dark.svg">
|
|
113
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/agents-light.svg" alt="Three panels. Shapley blame: the history agent gets 0.53, the other four agents 0.08 or less. Team belief in deploy: Dempster's rule gives 0.99 to 1.00 and 1.00 after a debate round; the cautious rule gives 0.79 to 0.95 and 0.84 to 0.97 after the debate. With only the logs and history agents replied, deploy, network and dns are all still possible.">
|
|
114
|
+
</picture>
|
|
115
|
+
|
|
116
|
+
The agent that matched an old DNS incident takes most of the blame. With only two replies in,
|
|
117
|
+
no cause beats the others yet, so the system waits or escalates. A debate round adds no new
|
|
118
|
+
evidence; Dempster's rule still counts it as new and reaches certainty, while the cautious
|
|
119
|
+
rule does not.
|
|
120
|
+
|
|
121
|
+
Two settings matter for local models. Instruct models put nearly all probability on one token,
|
|
122
|
+
so `--temperature T` flattens the scores (log-probabilities divided by T) before they become
|
|
123
|
+
beliefs. And five prompts on one base model are not five independent witnesses: Dempster's rule
|
|
124
|
+
(`--rule dempster`, the default, right for different models or non-overlapping evidence) fuses
|
|
125
|
+
four agents at 0.5–0.9 into 0.99, while the cautious rule (`--rule cautious`) does not count
|
|
126
|
+
their shared knowledge twice.
|
|
127
|
+
|
|
128
|
+
## The core idea
|
|
129
|
+
|
|
130
|
+
Everything is a **Capacity**: a lower set function `v` on the power set 2^Θ, stored as a dense
|
|
131
|
+
tensor `(..., 2**n)` whose index is the subset bitmask (`0` = ∅, `2**n − 1` = Θ). Each formalism
|
|
132
|
+
is a constructor that feeds in, and each summary is a view that reads out:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
probability ─┐ ┌─► pignistic / Shapley value
|
|
136
|
+
belief fn ─┤ ├─► Choquet lower / upper expectation
|
|
137
|
+
possibility ─┼─► Capacity (v, v*) ────┼─► Möbius mass, commonality, contour
|
|
138
|
+
credal set ─┤ ├─► credal vertices (core)
|
|
139
|
+
random interval (ℝ) ─┘ (via discretize) └─► Belnap (t, f) per event
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Möbius, zeta and commonality transforms are the fast O(n·2ⁿ) ones, applied axis by axis on a
|
|
143
|
+
`(2,)*n` reshape. All functions broadcast over leading batch dimensions and are differentiable.
|
|
144
|
+
|
|
145
|
+
## Feature tour
|
|
146
|
+
|
|
147
|
+
### Representations
|
|
148
|
+
|
|
149
|
+
| Object | What it is | Highlights |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `Frame(n, names)` | frame of discernment, n ≤ 16 | `subset([...])`, `elements(mask)`, membership/cardinality tables |
|
|
152
|
+
| mass tensors | `(..., 2**n)` Möbius masses | `vacuous`, `categorical`, `simple_support`, `from_probabilities`, `from_dirichlet`, `negation`, `random_mass`, `is_valid` |
|
|
153
|
+
| `Capacity` | any monotone lower set function | `from_mass / from_probability / from_possibility / from_credal`, `.upper`, `.dual()`, `.mass()`, `.commonality()`, `.contour()`, `.pignistic()`, `.credal_vertices()`, `.choquet(f)`, `.expectation_bounds(U)`, `.discount(α)` |
|
|
154
|
+
| structure checks | what kind of capacity is this? | `is_belief`, `is_probability`, `is_possibility`, `monotonicity_violation`, `convexity_violation` (2-monotonicity) |
|
|
155
|
+
| `possibility` | π distributions | t-norm conjunctive rules (min, product, Łukasiewicz), disjunctive, Dubois–Prade **adaptive** rule, Yager-style rule, discounting, consistency h |
|
|
156
|
+
| `RandomInterval` | random sets of intervals on ℝ (p-boxes) | `cdf_bounds` (the p-box), `prob_bounds([a,b])`, `mean_bounds`, `expectation_bounds(f)`, Dempster `conjunctive`, `mixture`, `from_pbox`, `discretize(edges)` → finite frame; optional sigmoid `temperature` for endpoint gradients |
|
|
157
|
+
| `SparseMass` | focal sets as int64 bitmasks, **n ≤ 62** | O(k₁k₂) `conjunctive / disjunctive / dempster / yager / dubois_prade`, `discount`, `belief / plausibility` of any event, `pignistic`, `expectation_bounds`, `summarize(k)` (outer approximation: Bel ↓, Pl ↑), `from_dense / to_dense` |
|
|
158
|
+
|
|
159
|
+
### Combination rules
|
|
160
|
+
|
|
161
|
+
All take masses `(..., 2**n)`; the two-source rules return `(mass, conflict)`. `tb.combine(list, F, rule)`
|
|
162
|
+
folds any of them over many sources. The rules differ mainly in what they do with the conflicting mass:
|
|
163
|
+
|
|
164
|
+
<picture>
|
|
165
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/rules-dark.svg">
|
|
166
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/rules-light.svg" alt="Stacked bars: two disagreeing sources combined by seven rules; each rule puts the conflicting mass somewhere different.">
|
|
167
|
+
</picture>
|
|
168
|
+
|
|
169
|
+
| Rule | Where the conflict goes | Assoc. | Idempotent | Use when |
|
|
170
|
+
|---|---|:-:|:-:|---|
|
|
171
|
+
| `conjunctive` | stays on ∅ (open world, TBM) | ✓ | | you want to *see* the conflict |
|
|
172
|
+
| `dempster` | renormalised away (gradient ∝ 1/(1−κ)) | ✓ | | independent, reliable, agreeing sources |
|
|
173
|
+
| `yager` | to Θ (ignorance); bounded gradients | | | conflict should mean "don't know" |
|
|
174
|
+
| `dubois_prade` | to A ∪ B of the conflicting pair | | | one of the two is probably right |
|
|
175
|
+
| `pcr5` / `pcr6` | back to A and B, proportionally (DSmT) | | | conflict should stay with whoever caused it |
|
|
176
|
+
| `cautious` | min of conjunctive weights (Denœux) | ✓ | ✓ | **non-distinct** sources (shared data/models) |
|
|
177
|
+
| `bold` | min of disjunctive weights | ✓ | ✓ | non-distinct sources, at least one reliable (disjunctive) |
|
|
178
|
+
| `disjunctive` | no conflict, A ∪ B | ✓ | | at least one source is right, unknown which |
|
|
179
|
+
| `average`, `murphy` | averaged, then Dempster (Murphy) | | | quick robust baseline |
|
|
180
|
+
|
|
181
|
+
Reliability: `discount(m, α)` (Shafer) and `contextual_discount(m, β)` (Mercier–Quost–Denœux:
|
|
182
|
+
per-class reliability, e.g. "this sensor is good at cars but bad at bikes"). The canonical
|
|
183
|
+
decomposition is exposed as `conjunctive_weights` / `from_conjunctive_weights`.
|
|
184
|
+
|
|
185
|
+
### Inconsistency: detect, measure, blame, resolve
|
|
186
|
+
|
|
187
|
+
| Function | What it does |
|
|
188
|
+
|---|---|
|
|
189
|
+
| `inconsistency.disagreement(a, b)` | sure-loss gap between two capacities. **Exact** for 2-monotone pairs (belief, possibility, probability): it is 0 iff the credal sets intersect (Frank's discrete sandwich theorem) |
|
|
190
|
+
| `disagreement_matrix(sources)` | all pairwise gaps `(..., S, S)` |
|
|
191
|
+
| `inconsistency_measures(sources)` | argumentation-style knowledge-base measures (Hunter–Konieczny): drastic, MI count, max, sum, *problematic* and *free* sources |
|
|
192
|
+
| `shapley_blame(sources, measure)` | Shapley inconsistency value per source; it sums to the total, and the outlier gets the blame |
|
|
193
|
+
| `blame_discount(sources)` | discount each source by its blame, then fuse with any rule |
|
|
194
|
+
| `resolve(a, b, tau, strategy, temperature)` | Dempster if the sources agree, otherwise a fallback (`yager`, credal `union`, `dubois_prade`, `pcr5`). `temperature` turns the hard switch into a differentiable blend |
|
|
195
|
+
| `credal_union`, `credal_intersection`, `specific_conflict`, `fuse_belief` | building blocks |
|
|
196
|
+
|
|
197
|
+
### Paraconsistent view: Belnap's four values
|
|
198
|
+
|
|
199
|
+
`torchbelief.belnap` reads each event as a bilattice value `(t, f) = (v(A), v(Ā))`. Pooling sources
|
|
200
|
+
with the knowledge join exposes contradiction instead of averaging it away.
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
x = belnap.pool(sources) # (n, 2) per singleton
|
|
204
|
+
belnap.label(x) # true / false / both / neither
|
|
205
|
+
belnap.degrees(x) # soft T, F, B, N (sums to 1)
|
|
206
|
+
belnap.contradiction(x), belnap.ignorance(x)
|
|
207
|
+
# bilattice algebra: negate, truth_and, truth_or, knowledge_join (⊕), knowledge_meet (⊗)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Decisions
|
|
211
|
+
|
|
212
|
+
Utilities are a matrix `U (acts, n)`; `-1` means abstain.
|
|
213
|
+
|
|
214
|
+
| | |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `choice` (any capacity) | `maximin` (Γ-maximin), `maximax`, `hurwicz(α)`, `pignistic_choice`, Walley `maximal_acts`, `interval_dominance_acts`, `e_admissible_acts`, **`decide_or_abstain(rule)`** |
|
|
217
|
+
| `decision` (masses) | `pignistic`, `plausibility_transform`, `interval` (Bel, Pl of singletons), `vacuity`, `expected_utility_bounds`, `decide(criterion)` |
|
|
218
|
+
| uncertainty measures | `nonspecificity`, `deng_entropy`, `pignistic_entropy`, `ambiguity_measure` (Jiroušek–Shenoy), `jousselme_distance`, `conflict_weight` |
|
|
219
|
+
|
|
220
|
+
### Neural layers and losses
|
|
221
|
+
|
|
222
|
+
| Layer | Output | Notes |
|
|
223
|
+
|---|---|---|
|
|
224
|
+
| `MassHead(d, F, focal)` | softmax over chosen focal sets | singletons + Θ, `"all"`, or an explicit list |
|
|
225
|
+
| `EvidentialHead(d, F)` | Dirichlet α → subjective-logic mass | evidential deep learning (Sensoy et al.) |
|
|
226
|
+
| `PrototypeHead(d, F, k)` | Denœux's **ENN**: distance-based evidence from k prototypes, fused by Dempster in closed form | m(Θ) → 1 away from the training data |
|
|
227
|
+
|
|
228
|
+
Losses: `pignistic_nll`, `belief_nll` (−log Bel{y}), `plausibility_nll` (−log Pl{y}),
|
|
229
|
+
`hurwicz_nll(λ)` interpolating the two, and `nonspecificity_penalty`.
|
|
230
|
+
|
|
231
|
+
From [`examples/evidential_classifier.py`](https://github.com/sulcantonin/torchbelief/blob/main/examples/evidential_classifier.py), probing far from the training blobs:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
EvidentialHead + MLP PrototypeHead (ENN)
|
|
235
|
+
[12, 12] m(Θ)=0.004 → a m(Θ)=1.000 → ABSTAIN
|
|
236
|
+
[-15, 5] m(Θ)=0.004 → b m(Θ)=0.999 → ABSTAIN
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The Dirichlet head extrapolates evidence linearly, just as a softmax does. The prototype head knows it is out of its depth.
|
|
240
|
+
|
|
241
|
+
## Examples
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
python examples/sensor_fusion.py # measures, blame, 5 rules × raw/discounted, Belnap table
|
|
245
|
+
python examples/evidential_classifier.py # Dirichlet head vs ENN, in- and out-of-distribution
|
|
246
|
+
python examples/tracking.py --replay docs/data/osorno-yolo11n.json # belief labels for saved YOLO tracks
|
|
247
|
+
python examples/tracking.py your.mp4 # live YOLO + ByteTrack (pip install ultralytics, AGPL-3.0)
|
|
248
|
+
python examples/multi_agent.py # LLM agents triage an outage (scripted replies, no model)
|
|
249
|
+
python examples/multi_agent.py --local Qwen/Qwen2.5-1.5B-Instruct --temperature 5 --rule cautious
|
|
250
|
+
python examples/multi_agent.py --replay docs/data/multi-agent-qwen2.5-1.5b.json --temperature 5 --rule cautious
|
|
251
|
+
python examples/multi_agent.py --live # Claude agents (pip install anthropic)
|
|
252
|
+
python docs/make_figures.py # regenerate every figure in docs/figures (needs matplotlib)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The [visual guide](https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md) walks through all of it with pictures and runnable snippets.
|
|
256
|
+
|
|
257
|
+
## Complexity and limits
|
|
258
|
+
|
|
259
|
+
| | cost | practical limit |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| dense transforms, conjunctive/disjunctive/Dempster/Yager | O(n·2ⁿ) | n ≤ 16 |
|
|
262
|
+
| Dubois–Prade, PCR5, `convexity_violation` | O(4ⁿ) memory | n ≲ 10 |
|
|
263
|
+
| PCR6 over S sources | O(2^{nS}) | small n·S |
|
|
264
|
+
| `credal_vertices`, `e_admissible_acts` | n! permutations | n ≲ 8 |
|
|
265
|
+
| `shapley_blame` | 2^S coalitions | S ≲ 12 sources |
|
|
266
|
+
| `SparseMass` | O(k₁k₂) per combination + `summarize(k)` | n ≤ 62 |
|
|
267
|
+
|
|
268
|
+
## Caveats
|
|
269
|
+
|
|
270
|
+
- `credal_vertices` (and so E-admissibility) is exact only for 2-monotone capacities.
|
|
271
|
+
- `credal_intersection` is a pointwise bound, not the full natural extension.
|
|
272
|
+
- Inconsistency *measures* are pairwise. Three sources can be jointly inconsistent while every pair is consistent.
|
|
273
|
+
- `cautious` needs non-dogmatic sources (m(Θ) > 0) and `bold` needs subnormal ones (m(∅) > 0). Discount first if necessary.
|
|
274
|
+
- `RandomInterval` queries are differentiable in the masses; for endpoint gradients pass a `temperature`.
|
|
275
|
+
|
|
276
|
+
## Roadmap
|
|
277
|
+
|
|
278
|
+
- learned compression of sparse combinations and box embeddings of focal sets (see [NOTES.md](https://github.com/sulcantonin/torchbelief/blob/main/NOTES.md))
|
|
279
|
+
- exact multi-source consistency and natural extension via a differentiable LP layer
|
|
280
|
+
- conditioning / Bayes-like updating of capacities (Dempster, geometric, generalized Bayes)
|
|
281
|
+
- continuous frames beyond intervals (random closed sets in ℝᵈ)
|
|
282
|
+
|
|
283
|
+
## Tests
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
pytest -q # 42 tests: brute-force checks of every rule, roundtrips, gradients, layers
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## References
|
|
290
|
+
|
|
291
|
+
Shafer 1976 · Smets & Kennes 1994 (TBM) · Yager 1987 · Dubois & Prade 1988 · Murphy 2000 ·
|
|
292
|
+
Smarandache & Dezert 2005 (PCR5) · Martin & Osswald 2006 (PCR6) · Denœux 2000 (ENN), 2008 (cautious/bold) ·
|
|
293
|
+
Mercier, Quost & Denœux 2008 (contextual discounting) · Walley 1991 · Troffaes 2007 (decision rules) ·
|
|
294
|
+
Frank 1982 (discrete sandwich theorem) · Hunter & Konieczny 2010 (Shapley inconsistency values) ·
|
|
295
|
+
Belnap 1977 · Ferson et al. 2003 (p-boxes) · Lowrance, Garvey & Strat 1986 (summarization) ·
|
|
296
|
+
Sensoy, Kaplan & Kandemir 2018 (evidential deep learning) · Jousselme et al. 2001.
|
|
297
|
+
|
|
298
|
+
## Citing
|
|
299
|
+
|
|
300
|
+
If you use torchbelief in research, please cite it. GitHub's "Cite this repository" button
|
|
301
|
+
gives APA and BibTeX from [`CITATION.cff`](https://github.com/sulcantonin/torchbelief/blob/main/CITATION.cff);
|
|
302
|
+
each release is archived on Zenodo with its own DOI.
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT © Antonin Sulc. Changes are listed in the [changelog](https://github.com/sulcantonin/torchbelief/blob/main/CHANGELOG.md).
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/logo/wordmark-dark.svg">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/logo/wordmark-light.svg" alt="torchbelief" width="420">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center"><b>Differentiable belief functions and imprecise probabilities for PyTorch.</b><br>
|
|
9
|
+
Fuse sources that disagree, find out which one is wrong, and decide, or abstain, when the evidence doesn't single out an answer.</p>
|
|
10
|
+
|
|
11
|
+
<p align="center"><b>New to belief functions? Start with the <a href="https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md">visual guide</a></b>: ten pictures, no theory required.</p>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<picture>
|
|
16
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/pipeline-dark.svg">
|
|
17
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/pipeline-light.svg" alt="Five steps: describe the evidence, convert it to a Capacity, check for disagreement, fuse the sources, decide or abstain.">
|
|
18
|
+
</picture>
|
|
19
|
+
|
|
20
|
+
A probability gives one number per event. `torchbelief` keeps **two**, a lower and an upper bound
|
|
21
|
+
`[Bel(A), Pl(A)]`, and carries them through fusion, conflict handling and decision making. Every
|
|
22
|
+
operation is a batched PyTorch op, so the whole pipeline is differentiable and can sit inside a network.
|
|
23
|
+
|
|
24
|
+
The logo shows the same idea: the solid inner flame is **belief**, the outlined outer flame is
|
|
25
|
+
**plausibility**, and the gap between them is the imprecision the library tracks.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install torchbelief # needs Python ≥ 3.9 and torch ≥ 2.0
|
|
31
|
+
pip install -e ".[dev]" # from a clone, + pytest
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
Four sensors classify an object; one of them is broken.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import torch, torchbelief as tb
|
|
40
|
+
from torchbelief import inconsistency as I, choice
|
|
41
|
+
|
|
42
|
+
F = tb.Frame(3, names=["car", "truck", "bike"])
|
|
43
|
+
lidar = tb.Capacity.from_mass(tb.simple_support(F, F.subset(["car"]), 0.7), F) # belief function
|
|
44
|
+
camera = tb.Capacity.from_probability(torch.tensor([0.75, 0.20, 0.05]), F) # probability
|
|
45
|
+
radar = tb.Capacity.from_possibility(torch.tensor([1.0, 0.6, 0.1]), F) # possibility
|
|
46
|
+
broken = tb.Capacity.from_mass(tb.simple_support(F, F.subset(["bike"]), 0.9), F) # faulty sensor
|
|
47
|
+
sources = [lidar, camera, radar, broken]
|
|
48
|
+
|
|
49
|
+
I.shapley_blame(sources) # tensor([0.050, 0.108, 0.083, 0.608]) ← the broken one
|
|
50
|
+
fixed, alpha = I.blame_discount(sources) # discount each source by its blame
|
|
51
|
+
m, conflict = tb.combine([s.mass() for s in fixed], F, rule="pcr6")
|
|
52
|
+
fused = tb.Capacity.from_mass(m, F)
|
|
53
|
+
|
|
54
|
+
fused.pignistic() # tensor([0.790, 0.103, 0.107])
|
|
55
|
+
fused.expectation_bounds(torch.eye(3)) # lower [0.73, 0.04, 0.08], upper [0.88, 0.19, 0.17]
|
|
56
|
+
choice.decide_or_abstain(fused, torch.eye(3)) # tensor(0) → "car" (−1 would mean abstain)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### On real video
|
|
60
|
+
|
|
61
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/tracking-demo.webp" alt="YOLO11n with ByteTrack on a rainy street. For each track a side panel shows YOLO's label this frame and torchbelief's label with its belief interval per class; where YOLO flips between car and truck, torchbelief shows car, or car or truck.">
|
|
62
|
+
|
|
63
|
+
YOLO with ByteTrack on real traffic. YOLO relabels every frame, so a hatchback flickers between
|
|
64
|
+
"car" and "truck". A belief function per track ([`examples/tracking.py`](https://github.com/sulcantonin/torchbelief/blob/main/examples/tracking.py),
|
|
65
|
+
two lines per frame) says "car or truck" while the evidence is split. Across the clip, YOLO
|
|
66
|
+
changes label 74 times and torchbelief 20 times. The [visual guide](https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md#9-real-video-labels-that-dont-flicker)
|
|
67
|
+
explains how.
|
|
68
|
+
|
|
69
|
+
### With a team of LLM agents
|
|
70
|
+
|
|
71
|
+
Five agents look for the root cause of an outage, each with one slice of the evidence: logs,
|
|
72
|
+
metrics, the deploy diff, network flows, past incidents. [`examples/multi_agent.py`](https://github.com/sulcantonin/torchbelief/blob/main/examples/multi_agent.py)
|
|
73
|
+
turns each reply into a belief function over the causes `deploy, database, network, dns, cache`:
|
|
74
|
+
|
|
75
|
+
- **API models** (`--live`, Claude) name a *set* of plausible causes and a confidence. The set is a
|
|
76
|
+
focal set, so "not the network" and "can't tell" need no special handling.
|
|
77
|
+
- **Local models** (`--local`, any Hugging Face causal LM) are not asked for a confidence. Each
|
|
78
|
+
cause name is scored by its token log-probability after the prompt. The probability the model
|
|
79
|
+
puts on *none* of the names is treated as ignorance, so an agent that would rather answer
|
|
80
|
+
something else is discounted automatically.
|
|
81
|
+
|
|
82
|
+
With Qwen2.5-1.5B-Instruct as every agent (`--temperature 5`, replies saved in
|
|
83
|
+
[`docs/data`](https://github.com/sulcantonin/torchbelief/blob/main/docs/data/multi-agent-qwen2.5-1.5b.json)):
|
|
84
|
+
|
|
85
|
+
<picture>
|
|
86
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/agents-dark.svg">
|
|
87
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/agents-light.svg" alt="Three panels. Shapley blame: the history agent gets 0.53, the other four agents 0.08 or less. Team belief in deploy: Dempster's rule gives 0.99 to 1.00 and 1.00 after a debate round; the cautious rule gives 0.79 to 0.95 and 0.84 to 0.97 after the debate. With only the logs and history agents replied, deploy, network and dns are all still possible.">
|
|
88
|
+
</picture>
|
|
89
|
+
|
|
90
|
+
The agent that matched an old DNS incident takes most of the blame. With only two replies in,
|
|
91
|
+
no cause beats the others yet, so the system waits or escalates. A debate round adds no new
|
|
92
|
+
evidence; Dempster's rule still counts it as new and reaches certainty, while the cautious
|
|
93
|
+
rule does not.
|
|
94
|
+
|
|
95
|
+
Two settings matter for local models. Instruct models put nearly all probability on one token,
|
|
96
|
+
so `--temperature T` flattens the scores (log-probabilities divided by T) before they become
|
|
97
|
+
beliefs. And five prompts on one base model are not five independent witnesses: Dempster's rule
|
|
98
|
+
(`--rule dempster`, the default, right for different models or non-overlapping evidence) fuses
|
|
99
|
+
four agents at 0.5–0.9 into 0.99, while the cautious rule (`--rule cautious`) does not count
|
|
100
|
+
their shared knowledge twice.
|
|
101
|
+
|
|
102
|
+
## The core idea
|
|
103
|
+
|
|
104
|
+
Everything is a **Capacity**: a lower set function `v` on the power set 2^Θ, stored as a dense
|
|
105
|
+
tensor `(..., 2**n)` whose index is the subset bitmask (`0` = ∅, `2**n − 1` = Θ). Each formalism
|
|
106
|
+
is a constructor that feeds in, and each summary is a view that reads out:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
probability ─┐ ┌─► pignistic / Shapley value
|
|
110
|
+
belief fn ─┤ ├─► Choquet lower / upper expectation
|
|
111
|
+
possibility ─┼─► Capacity (v, v*) ────┼─► Möbius mass, commonality, contour
|
|
112
|
+
credal set ─┤ ├─► credal vertices (core)
|
|
113
|
+
random interval (ℝ) ─┘ (via discretize) └─► Belnap (t, f) per event
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Möbius, zeta and commonality transforms are the fast O(n·2ⁿ) ones, applied axis by axis on a
|
|
117
|
+
`(2,)*n` reshape. All functions broadcast over leading batch dimensions and are differentiable.
|
|
118
|
+
|
|
119
|
+
## Feature tour
|
|
120
|
+
|
|
121
|
+
### Representations
|
|
122
|
+
|
|
123
|
+
| Object | What it is | Highlights |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `Frame(n, names)` | frame of discernment, n ≤ 16 | `subset([...])`, `elements(mask)`, membership/cardinality tables |
|
|
126
|
+
| mass tensors | `(..., 2**n)` Möbius masses | `vacuous`, `categorical`, `simple_support`, `from_probabilities`, `from_dirichlet`, `negation`, `random_mass`, `is_valid` |
|
|
127
|
+
| `Capacity` | any monotone lower set function | `from_mass / from_probability / from_possibility / from_credal`, `.upper`, `.dual()`, `.mass()`, `.commonality()`, `.contour()`, `.pignistic()`, `.credal_vertices()`, `.choquet(f)`, `.expectation_bounds(U)`, `.discount(α)` |
|
|
128
|
+
| structure checks | what kind of capacity is this? | `is_belief`, `is_probability`, `is_possibility`, `monotonicity_violation`, `convexity_violation` (2-monotonicity) |
|
|
129
|
+
| `possibility` | π distributions | t-norm conjunctive rules (min, product, Łukasiewicz), disjunctive, Dubois–Prade **adaptive** rule, Yager-style rule, discounting, consistency h |
|
|
130
|
+
| `RandomInterval` | random sets of intervals on ℝ (p-boxes) | `cdf_bounds` (the p-box), `prob_bounds([a,b])`, `mean_bounds`, `expectation_bounds(f)`, Dempster `conjunctive`, `mixture`, `from_pbox`, `discretize(edges)` → finite frame; optional sigmoid `temperature` for endpoint gradients |
|
|
131
|
+
| `SparseMass` | focal sets as int64 bitmasks, **n ≤ 62** | O(k₁k₂) `conjunctive / disjunctive / dempster / yager / dubois_prade`, `discount`, `belief / plausibility` of any event, `pignistic`, `expectation_bounds`, `summarize(k)` (outer approximation: Bel ↓, Pl ↑), `from_dense / to_dense` |
|
|
132
|
+
|
|
133
|
+
### Combination rules
|
|
134
|
+
|
|
135
|
+
All take masses `(..., 2**n)`; the two-source rules return `(mass, conflict)`. `tb.combine(list, F, rule)`
|
|
136
|
+
folds any of them over many sources. The rules differ mainly in what they do with the conflicting mass:
|
|
137
|
+
|
|
138
|
+
<picture>
|
|
139
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/rules-dark.svg">
|
|
140
|
+
<img src="https://raw.githubusercontent.com/sulcantonin/torchbelief/main/docs/figures/rules-light.svg" alt="Stacked bars: two disagreeing sources combined by seven rules; each rule puts the conflicting mass somewhere different.">
|
|
141
|
+
</picture>
|
|
142
|
+
|
|
143
|
+
| Rule | Where the conflict goes | Assoc. | Idempotent | Use when |
|
|
144
|
+
|---|---|:-:|:-:|---|
|
|
145
|
+
| `conjunctive` | stays on ∅ (open world, TBM) | ✓ | | you want to *see* the conflict |
|
|
146
|
+
| `dempster` | renormalised away (gradient ∝ 1/(1−κ)) | ✓ | | independent, reliable, agreeing sources |
|
|
147
|
+
| `yager` | to Θ (ignorance); bounded gradients | | | conflict should mean "don't know" |
|
|
148
|
+
| `dubois_prade` | to A ∪ B of the conflicting pair | | | one of the two is probably right |
|
|
149
|
+
| `pcr5` / `pcr6` | back to A and B, proportionally (DSmT) | | | conflict should stay with whoever caused it |
|
|
150
|
+
| `cautious` | min of conjunctive weights (Denœux) | ✓ | ✓ | **non-distinct** sources (shared data/models) |
|
|
151
|
+
| `bold` | min of disjunctive weights | ✓ | ✓ | non-distinct sources, at least one reliable (disjunctive) |
|
|
152
|
+
| `disjunctive` | no conflict, A ∪ B | ✓ | | at least one source is right, unknown which |
|
|
153
|
+
| `average`, `murphy` | averaged, then Dempster (Murphy) | | | quick robust baseline |
|
|
154
|
+
|
|
155
|
+
Reliability: `discount(m, α)` (Shafer) and `contextual_discount(m, β)` (Mercier–Quost–Denœux:
|
|
156
|
+
per-class reliability, e.g. "this sensor is good at cars but bad at bikes"). The canonical
|
|
157
|
+
decomposition is exposed as `conjunctive_weights` / `from_conjunctive_weights`.
|
|
158
|
+
|
|
159
|
+
### Inconsistency: detect, measure, blame, resolve
|
|
160
|
+
|
|
161
|
+
| Function | What it does |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `inconsistency.disagreement(a, b)` | sure-loss gap between two capacities. **Exact** for 2-monotone pairs (belief, possibility, probability): it is 0 iff the credal sets intersect (Frank's discrete sandwich theorem) |
|
|
164
|
+
| `disagreement_matrix(sources)` | all pairwise gaps `(..., S, S)` |
|
|
165
|
+
| `inconsistency_measures(sources)` | argumentation-style knowledge-base measures (Hunter–Konieczny): drastic, MI count, max, sum, *problematic* and *free* sources |
|
|
166
|
+
| `shapley_blame(sources, measure)` | Shapley inconsistency value per source; it sums to the total, and the outlier gets the blame |
|
|
167
|
+
| `blame_discount(sources)` | discount each source by its blame, then fuse with any rule |
|
|
168
|
+
| `resolve(a, b, tau, strategy, temperature)` | Dempster if the sources agree, otherwise a fallback (`yager`, credal `union`, `dubois_prade`, `pcr5`). `temperature` turns the hard switch into a differentiable blend |
|
|
169
|
+
| `credal_union`, `credal_intersection`, `specific_conflict`, `fuse_belief` | building blocks |
|
|
170
|
+
|
|
171
|
+
### Paraconsistent view: Belnap's four values
|
|
172
|
+
|
|
173
|
+
`torchbelief.belnap` reads each event as a bilattice value `(t, f) = (v(A), v(Ā))`. Pooling sources
|
|
174
|
+
with the knowledge join exposes contradiction instead of averaging it away.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
x = belnap.pool(sources) # (n, 2) per singleton
|
|
178
|
+
belnap.label(x) # true / false / both / neither
|
|
179
|
+
belnap.degrees(x) # soft T, F, B, N (sums to 1)
|
|
180
|
+
belnap.contradiction(x), belnap.ignorance(x)
|
|
181
|
+
# bilattice algebra: negate, truth_and, truth_or, knowledge_join (⊕), knowledge_meet (⊗)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Decisions
|
|
185
|
+
|
|
186
|
+
Utilities are a matrix `U (acts, n)`; `-1` means abstain.
|
|
187
|
+
|
|
188
|
+
| | |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `choice` (any capacity) | `maximin` (Γ-maximin), `maximax`, `hurwicz(α)`, `pignistic_choice`, Walley `maximal_acts`, `interval_dominance_acts`, `e_admissible_acts`, **`decide_or_abstain(rule)`** |
|
|
191
|
+
| `decision` (masses) | `pignistic`, `plausibility_transform`, `interval` (Bel, Pl of singletons), `vacuity`, `expected_utility_bounds`, `decide(criterion)` |
|
|
192
|
+
| uncertainty measures | `nonspecificity`, `deng_entropy`, `pignistic_entropy`, `ambiguity_measure` (Jiroušek–Shenoy), `jousselme_distance`, `conflict_weight` |
|
|
193
|
+
|
|
194
|
+
### Neural layers and losses
|
|
195
|
+
|
|
196
|
+
| Layer | Output | Notes |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `MassHead(d, F, focal)` | softmax over chosen focal sets | singletons + Θ, `"all"`, or an explicit list |
|
|
199
|
+
| `EvidentialHead(d, F)` | Dirichlet α → subjective-logic mass | evidential deep learning (Sensoy et al.) |
|
|
200
|
+
| `PrototypeHead(d, F, k)` | Denœux's **ENN**: distance-based evidence from k prototypes, fused by Dempster in closed form | m(Θ) → 1 away from the training data |
|
|
201
|
+
|
|
202
|
+
Losses: `pignistic_nll`, `belief_nll` (−log Bel{y}), `plausibility_nll` (−log Pl{y}),
|
|
203
|
+
`hurwicz_nll(λ)` interpolating the two, and `nonspecificity_penalty`.
|
|
204
|
+
|
|
205
|
+
From [`examples/evidential_classifier.py`](https://github.com/sulcantonin/torchbelief/blob/main/examples/evidential_classifier.py), probing far from the training blobs:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
EvidentialHead + MLP PrototypeHead (ENN)
|
|
209
|
+
[12, 12] m(Θ)=0.004 → a m(Θ)=1.000 → ABSTAIN
|
|
210
|
+
[-15, 5] m(Θ)=0.004 → b m(Θ)=0.999 → ABSTAIN
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The Dirichlet head extrapolates evidence linearly, just as a softmax does. The prototype head knows it is out of its depth.
|
|
214
|
+
|
|
215
|
+
## Examples
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python examples/sensor_fusion.py # measures, blame, 5 rules × raw/discounted, Belnap table
|
|
219
|
+
python examples/evidential_classifier.py # Dirichlet head vs ENN, in- and out-of-distribution
|
|
220
|
+
python examples/tracking.py --replay docs/data/osorno-yolo11n.json # belief labels for saved YOLO tracks
|
|
221
|
+
python examples/tracking.py your.mp4 # live YOLO + ByteTrack (pip install ultralytics, AGPL-3.0)
|
|
222
|
+
python examples/multi_agent.py # LLM agents triage an outage (scripted replies, no model)
|
|
223
|
+
python examples/multi_agent.py --local Qwen/Qwen2.5-1.5B-Instruct --temperature 5 --rule cautious
|
|
224
|
+
python examples/multi_agent.py --replay docs/data/multi-agent-qwen2.5-1.5b.json --temperature 5 --rule cautious
|
|
225
|
+
python examples/multi_agent.py --live # Claude agents (pip install anthropic)
|
|
226
|
+
python docs/make_figures.py # regenerate every figure in docs/figures (needs matplotlib)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The [visual guide](https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md) walks through all of it with pictures and runnable snippets.
|
|
230
|
+
|
|
231
|
+
## Complexity and limits
|
|
232
|
+
|
|
233
|
+
| | cost | practical limit |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| dense transforms, conjunctive/disjunctive/Dempster/Yager | O(n·2ⁿ) | n ≤ 16 |
|
|
236
|
+
| Dubois–Prade, PCR5, `convexity_violation` | O(4ⁿ) memory | n ≲ 10 |
|
|
237
|
+
| PCR6 over S sources | O(2^{nS}) | small n·S |
|
|
238
|
+
| `credal_vertices`, `e_admissible_acts` | n! permutations | n ≲ 8 |
|
|
239
|
+
| `shapley_blame` | 2^S coalitions | S ≲ 12 sources |
|
|
240
|
+
| `SparseMass` | O(k₁k₂) per combination + `summarize(k)` | n ≤ 62 |
|
|
241
|
+
|
|
242
|
+
## Caveats
|
|
243
|
+
|
|
244
|
+
- `credal_vertices` (and so E-admissibility) is exact only for 2-monotone capacities.
|
|
245
|
+
- `credal_intersection` is a pointwise bound, not the full natural extension.
|
|
246
|
+
- Inconsistency *measures* are pairwise. Three sources can be jointly inconsistent while every pair is consistent.
|
|
247
|
+
- `cautious` needs non-dogmatic sources (m(Θ) > 0) and `bold` needs subnormal ones (m(∅) > 0). Discount first if necessary.
|
|
248
|
+
- `RandomInterval` queries are differentiable in the masses; for endpoint gradients pass a `temperature`.
|
|
249
|
+
|
|
250
|
+
## Roadmap
|
|
251
|
+
|
|
252
|
+
- learned compression of sparse combinations and box embeddings of focal sets (see [NOTES.md](https://github.com/sulcantonin/torchbelief/blob/main/NOTES.md))
|
|
253
|
+
- exact multi-source consistency and natural extension via a differentiable LP layer
|
|
254
|
+
- conditioning / Bayes-like updating of capacities (Dempster, geometric, generalized Bayes)
|
|
255
|
+
- continuous frames beyond intervals (random closed sets in ℝᵈ)
|
|
256
|
+
|
|
257
|
+
## Tests
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
pytest -q # 42 tests: brute-force checks of every rule, roundtrips, gradients, layers
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## References
|
|
264
|
+
|
|
265
|
+
Shafer 1976 · Smets & Kennes 1994 (TBM) · Yager 1987 · Dubois & Prade 1988 · Murphy 2000 ·
|
|
266
|
+
Smarandache & Dezert 2005 (PCR5) · Martin & Osswald 2006 (PCR6) · Denœux 2000 (ENN), 2008 (cautious/bold) ·
|
|
267
|
+
Mercier, Quost & Denœux 2008 (contextual discounting) · Walley 1991 · Troffaes 2007 (decision rules) ·
|
|
268
|
+
Frank 1982 (discrete sandwich theorem) · Hunter & Konieczny 2010 (Shapley inconsistency values) ·
|
|
269
|
+
Belnap 1977 · Ferson et al. 2003 (p-boxes) · Lowrance, Garvey & Strat 1986 (summarization) ·
|
|
270
|
+
Sensoy, Kaplan & Kandemir 2018 (evidential deep learning) · Jousselme et al. 2001.
|
|
271
|
+
|
|
272
|
+
## Citing
|
|
273
|
+
|
|
274
|
+
If you use torchbelief in research, please cite it. GitHub's "Cite this repository" button
|
|
275
|
+
gives APA and BibTeX from [`CITATION.cff`](https://github.com/sulcantonin/torchbelief/blob/main/CITATION.cff);
|
|
276
|
+
each release is archived on Zenodo with its own DOI.
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT © Antonin Sulc. Changes are listed in the [changelog](https://github.com/sulcantonin/torchbelief/blob/main/CHANGELOG.md).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "torchbelief"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Differentiable belief functions (Dempster-Shafer, Yager, TBM, Dubois-Prade, ...) and decision rules in PyTorch"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Antonin Sulc" }]
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
keywords = ["belief functions", "Dempster-Shafer", "evidential", "uncertainty", "pytorch",
|
|
15
|
+
"imprecise probability", "sensor fusion"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
24
|
+
]
|
|
25
|
+
dependencies = ["torch>=2.0"]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = ["pytest>=7"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/sulcantonin/torchbelief"
|
|
32
|
+
Documentation = "https://github.com/sulcantonin/torchbelief/blob/main/docs/visual-guide.md"
|
|
33
|
+
Changelog = "https://github.com/sulcantonin/torchbelief/blob/main/CHANGELOG.md"
|
|
34
|
+
Issues = "https://github.com/sulcantonin/torchbelief/issues"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools]
|
|
37
|
+
packages = ["torchbelief"]
|