probabilistic_machines 0.1.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +58 -0
- data/Cargo.lock +294 -0
- data/Cargo.toml +24 -0
- data/LICENSE.txt +21 -0
- data/README.md +203 -0
- data/ext/probabilistic_machines_native/core/Cargo.toml +24 -0
- data/ext/probabilistic_machines_native/core/src/countmin.rs +240 -0
- data/ext/probabilistic_machines_native/core/src/cuckoo.rs +283 -0
- data/ext/probabilistic_machines_native/core/src/hash.rs +88 -0
- data/ext/probabilistic_machines_native/core/src/hyperloglog.rs +254 -0
- data/ext/probabilistic_machines_native/core/src/lib.rs +65 -0
- data/ext/probabilistic_machines_native/core/src/tdigest.rs +445 -0
- data/ext/probabilistic_machines_native/core/src/topk.rs +325 -0
- data/ext/probabilistic_machines_native/extconf.rb +3 -0
- data/ext/probabilistic_machines_native/ffi/Cargo.toml +20 -0
- data/ext/probabilistic_machines_native/ffi/extconf.rb +21 -0
- data/ext/probabilistic_machines_native/ffi/src/countmin.rs +188 -0
- data/ext/probabilistic_machines_native/ffi/src/cuckoo.rs +149 -0
- data/ext/probabilistic_machines_native/ffi/src/data.rs +69 -0
- data/ext/probabilistic_machines_native/ffi/src/hyperloglog.rs +155 -0
- data/ext/probabilistic_machines_native/ffi/src/lib.rs +140 -0
- data/ext/probabilistic_machines_native/ffi/src/tdigest.rs +166 -0
- data/ext/probabilistic_machines_native/ffi/src/topk.rs +299 -0
- data/lib/probabilistic_machines/version.rb +5 -0
- data/lib/probabilistic_machines.rb +7 -0
- data/sig/probabilistic_machines.rbs +182 -0
- metadata +90 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8c5cfb3b4e94463e5c61a5a8181c1bd713d0135b87839176d55b1a4ac9ed1fe9
|
|
4
|
+
data.tar.gz: a086f4a5d9cc1505c740bd4fb7be35b84283fae0fbbc5888e04ad44d0333ced9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2619d85be86268533cc95d101d332ceb71e50139a24487d1fecb9f281d501a511fda27bdb9191e865b5a1dae265faa80f2e632bb5b1a5586a9817f7a817d85e2
|
|
7
|
+
data.tar.gz: 73164970f1cfa50eb8a1a976eddff1e053865af8f122393bcdd4b5cf8db133c1360ea03b09407d457a8bf245bf85685206962c70821f3e63e5d6493088a7b943
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.1](https://github.com/seuros/probabilistic_machines/compare/probabilistic_machines-v0.1.0...probabilistic_machines/v0.1.1) (2026-11-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* HeavyHitters codec and an infallible exact-size encode ([1cf9442](https://github.com/seuros/probabilistic_machines/commit/1cf944208e191f1cc7847221239204e14f0ad9a9))
|
|
9
|
+
* probabilistic data structures in no_std Rust ([e8c9711](https://github.com/seuros/probabilistic_machines/commit/e8c97117a19e39ecfb2eff588724d9bd3d33be48))
|
|
10
|
+
* Ruby gem and magnus FFI for the five structures ([83144bd](https://github.com/seuros/probabilistic_machines/commit/83144bd4553f2ea51fd8437ab966142b50122cec))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* a loaded t-digest honours the compression its constructor requires ([dae877e](https://github.com/seuros/probabilistic_machines/commit/dae877eb1161dfc16f5238329c8c6fdd4b1c78e5))
|
|
16
|
+
* a source install can build, and a failed gem push fails the release ([4b18e42](https://github.com/seuros/probabilistic_machines/commit/4b18e42a9b4c7d6264cc3cc9f685e0cb597de409))
|
|
17
|
+
* a zero cluster budget spun the t-digest fold loop forever ([8f4f146](https://github.com/seuros/probabilistic_machines/commit/8f4f1466ca579a943d8a23aec4075b53314e3824))
|
|
18
|
+
* expose and persist count-min saturation ([0ac83ac](https://github.com/seuros/probabilistic_machines/commit/0ac83ac77d02ddee5d7614d73e95e5d103e823e8))
|
|
19
|
+
* fold t-digest clusters without overflowing their span ([2e33539](https://github.com/seuros/probabilistic_machines/commit/2e33539b0dbcc0eb8d288eedc936c0ece8d7f7bd))
|
|
20
|
+
* interpolate a subnormal t-digest span without underflowing it ([1b05f4e](https://github.com/seuros/probabilistic_machines/commit/1b05f4e5515fa4468730bec3708daf8f57052726))
|
|
21
|
+
* interpolate the t-digest cdf without overflowing the span ([48cd8a1](https://github.com/seuros/probabilistic_machines/commit/48cd8a11f1b2fdd56403af46614fd6a56b8b8309))
|
|
22
|
+
* keep t-digest total weight finite and the cluster count bounded ([0a37d4b](https://github.com/seuros/probabilistic_machines/commit/0a37d4b52cd5a0abd7de862fe0f52e4f987c956e))
|
|
23
|
+
* refuse a fractional compression too ([379c001](https://github.com/seuros/probabilistic_machines/commit/379c0012d2e5c21131ec8b9fb92534e796997be9))
|
|
24
|
+
* refuse a fractional count instead of truncating it to zero ([0b88ae8](https://github.com/seuros/probabilistic_machines/commit/0b88ae8204ebbb761e8aab42df3e7d014501d135))
|
|
25
|
+
* reject a compression a t-digest cannot honour ([f53e07b](https://github.com/seuros/probabilistic_machines/commit/f53e07b3af5377565253e14c7327b90c9975eb95))
|
|
26
|
+
* reject a hyperloglog rank its precision cannot reach ([c41210b](https://github.com/seuros/probabilistic_machines/commit/c41210b66ccfc305240244fcf151990ea317392e))
|
|
27
|
+
* reject a t-digest frame that lost whole clusters ([2e7e0b5](https://github.com/seuros/probabilistic_machines/commit/2e7e0b5594ff14297d98cb5c32380beed1f8192a))
|
|
28
|
+
* reject a t-digest frame whose total weight cannot exist ([4bdf21f](https://github.com/seuros/probabilistic_machines/commit/4bdf21fa69949159aa30a8d400a8c7d11e64fbf1))
|
|
29
|
+
* reject count-min counters its total cannot explain ([4413fe9](https://github.com/seuros/probabilistic_machines/commit/4413fe965cfdad617f46d9f2f2720e13a3aee6d0))
|
|
30
|
+
* reject heavy-hitter records no stream could leave ([3a4172e](https://github.com/seuros/probabilistic_machines/commit/3a4172e730fd7c46b06d39bde2ef2c6034be19d4))
|
|
31
|
+
* ship the core's test files so the packaged crate builds ([b567b39](https://github.com/seuros/probabilistic_machines/commit/b567b39144fd403f92efcb7ac48120be228d3b6f))
|
|
32
|
+
* t-digest overflow and NaN, and report count-min saturation ([f3aa87c](https://github.com/seuros/probabilistic_machines/commit/f3aa87c018465de0618125b3f2f84e6848e57f37))
|
|
33
|
+
* the t-digest arithmetic builds without std ([8009555](https://github.com/seuros/probabilistic_machines/commit/800955549c872ff7db8d257fbe63b18d326c1aa2))
|
|
34
|
+
* validate HLL registers on decode, and the same hash collision ([496c077](https://github.com/seuros/probabilistic_machines/commit/496c0771f9a2c06766c1296c7123ea3b6bd7f4ae))
|
|
35
|
+
* widen heavy-hitter bounds for what a merged table evicted ([41b0a9c](https://github.com/seuros/probabilistic_machines/commit/41b0a9c33f7433b915f448820628604c107d1cad))
|
|
36
|
+
|
|
37
|
+
## 0.1.0
|
|
38
|
+
|
|
39
|
+
- Initial release.
|
|
40
|
+
- `ProbabilisticMachines::HyperLogLog`: distinct counts in 1024, 4096, 16384 or
|
|
41
|
+
65536 registers, with `merge` for union and `standard_error` for the bound.
|
|
42
|
+
- `ProbabilisticMachines::CountMinSketch`: per-item frequencies in a width of
|
|
43
|
+
1024, 4096 or 16384 by a depth of 4 or 8.
|
|
44
|
+
- `ProbabilisticMachines::TDigest`: quantiles and CDF from clusters, with a
|
|
45
|
+
runtime compression.
|
|
46
|
+
- `ProbabilisticMachines::CuckooFilter`: membership that can forget, in 1024,
|
|
47
|
+
4096, 16384 or 65536 buckets, raising `ProbabilisticMachines::Full` when a
|
|
48
|
+
fingerprint has nowhere to go.
|
|
49
|
+
- `ProbabilisticMachines::HeavyHitters`: Space-Saving top-k over 16, 64 or 256
|
|
50
|
+
counters, returning `Hitter` objects that carry the overestimate.
|
|
51
|
+
- `dump`/`load` round-trip every sketch through the byte format of the
|
|
52
|
+
`probabilistic-machines` crate. A `HeavyHitters` blob is the crate's frame
|
|
53
|
+
followed by the extension's key table, so the tracked strings survive a
|
|
54
|
+
restart instead of loading back as hashes.
|
|
55
|
+
- RBS signatures for the whole public API.
|
|
56
|
+
- The `probabilistic-machines` crate codes `HeavyHitters<u64, K>`, and with
|
|
57
|
+
`alloc` hands every structure's frame back from `encoded` instead of filling
|
|
58
|
+
a caller's buffer, with `decode_into` to read one back in place.
|
data/Cargo.lock
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "aho-corasick"
|
|
7
|
+
version = "1.1.5"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"memchr",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "bindgen"
|
|
16
|
+
version = "0.72.1"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"bitflags",
|
|
21
|
+
"cexpr",
|
|
22
|
+
"clang-sys",
|
|
23
|
+
"itertools",
|
|
24
|
+
"proc-macro2",
|
|
25
|
+
"quote",
|
|
26
|
+
"regex",
|
|
27
|
+
"rustc-hash",
|
|
28
|
+
"shlex",
|
|
29
|
+
"syn",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "bitflags"
|
|
34
|
+
version = "2.13.2"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
|
37
|
+
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "bytes"
|
|
40
|
+
version = "1.12.1"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "cexpr"
|
|
46
|
+
version = "0.6.0"
|
|
47
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
|
+
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
|
49
|
+
dependencies = [
|
|
50
|
+
"nom",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[[package]]
|
|
54
|
+
name = "cfg-if"
|
|
55
|
+
version = "1.0.5"
|
|
56
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
|
+
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "clang-sys"
|
|
61
|
+
version = "1.9.1"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"glob",
|
|
66
|
+
"libc",
|
|
67
|
+
"libloading",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[[package]]
|
|
71
|
+
name = "either"
|
|
72
|
+
version = "1.19.0"
|
|
73
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
74
|
+
checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
|
|
75
|
+
|
|
76
|
+
[[package]]
|
|
77
|
+
name = "glob"
|
|
78
|
+
version = "0.3.4"
|
|
79
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
80
|
+
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
|
81
|
+
|
|
82
|
+
[[package]]
|
|
83
|
+
name = "itertools"
|
|
84
|
+
version = "0.13.0"
|
|
85
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
86
|
+
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
87
|
+
dependencies = [
|
|
88
|
+
"either",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[[package]]
|
|
92
|
+
name = "lazy_static"
|
|
93
|
+
version = "1.5.1"
|
|
94
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
95
|
+
checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "libc"
|
|
99
|
+
version = "0.2.191"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
|
|
102
|
+
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "libloading"
|
|
105
|
+
version = "0.8.9"
|
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
107
|
+
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
108
|
+
dependencies = [
|
|
109
|
+
"cfg-if",
|
|
110
|
+
"windows-link",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
[[package]]
|
|
114
|
+
name = "magnus"
|
|
115
|
+
version = "0.9.3"
|
|
116
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
117
|
+
checksum = "40ef424404d166cbb71c8b8d19c9c589c748c4547c1afa1928c5504299f6c713"
|
|
118
|
+
dependencies = [
|
|
119
|
+
"bytes",
|
|
120
|
+
"magnus-macros",
|
|
121
|
+
"rb-sys",
|
|
122
|
+
"rb-sys-env",
|
|
123
|
+
"seq-macro",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
[[package]]
|
|
127
|
+
name = "magnus-macros"
|
|
128
|
+
version = "0.9.1"
|
|
129
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
130
|
+
checksum = "9dafde2eeb66f090e0f326112323eb8324b12cb32b5d1210e541d3df6b0ce77e"
|
|
131
|
+
dependencies = [
|
|
132
|
+
"proc-macro2",
|
|
133
|
+
"quote",
|
|
134
|
+
"syn",
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
[[package]]
|
|
138
|
+
name = "memchr"
|
|
139
|
+
version = "2.8.3"
|
|
140
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
141
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
142
|
+
|
|
143
|
+
[[package]]
|
|
144
|
+
name = "minimal-lexical"
|
|
145
|
+
version = "0.2.1"
|
|
146
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
147
|
+
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|
148
|
+
|
|
149
|
+
[[package]]
|
|
150
|
+
name = "nom"
|
|
151
|
+
version = "7.1.3"
|
|
152
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
153
|
+
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
|
154
|
+
dependencies = [
|
|
155
|
+
"memchr",
|
|
156
|
+
"minimal-lexical",
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
[[package]]
|
|
160
|
+
name = "probabilistic-machines"
|
|
161
|
+
version = "0.2.0"
|
|
162
|
+
|
|
163
|
+
[[package]]
|
|
164
|
+
name = "probabilistic_machines_native"
|
|
165
|
+
version = "0.1.1"
|
|
166
|
+
dependencies = [
|
|
167
|
+
"bytes",
|
|
168
|
+
"magnus",
|
|
169
|
+
"probabilistic-machines",
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
[[package]]
|
|
173
|
+
name = "proc-macro2"
|
|
174
|
+
version = "1.0.107"
|
|
175
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
176
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
177
|
+
dependencies = [
|
|
178
|
+
"unicode-ident",
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
[[package]]
|
|
182
|
+
name = "quote"
|
|
183
|
+
version = "1.0.47"
|
|
184
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
186
|
+
dependencies = [
|
|
187
|
+
"proc-macro2",
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
[[package]]
|
|
191
|
+
name = "rb-sys"
|
|
192
|
+
version = "0.9.130"
|
|
193
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
194
|
+
checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
|
|
195
|
+
dependencies = [
|
|
196
|
+
"rb-sys-build",
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
[[package]]
|
|
200
|
+
name = "rb-sys-build"
|
|
201
|
+
version = "0.9.130"
|
|
202
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
203
|
+
checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
|
|
204
|
+
dependencies = [
|
|
205
|
+
"bindgen",
|
|
206
|
+
"lazy_static",
|
|
207
|
+
"proc-macro2",
|
|
208
|
+
"quote",
|
|
209
|
+
"regex",
|
|
210
|
+
"shell-words",
|
|
211
|
+
"syn",
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
[[package]]
|
|
215
|
+
name = "rb-sys-env"
|
|
216
|
+
version = "0.2.3"
|
|
217
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
218
|
+
checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
|
|
219
|
+
|
|
220
|
+
[[package]]
|
|
221
|
+
name = "regex"
|
|
222
|
+
version = "1.13.1"
|
|
223
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
224
|
+
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
|
225
|
+
dependencies = [
|
|
226
|
+
"aho-corasick",
|
|
227
|
+
"memchr",
|
|
228
|
+
"regex-automata",
|
|
229
|
+
"regex-syntax",
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
[[package]]
|
|
233
|
+
name = "regex-automata"
|
|
234
|
+
version = "0.4.18"
|
|
235
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
236
|
+
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
|
237
|
+
dependencies = [
|
|
238
|
+
"aho-corasick",
|
|
239
|
+
"memchr",
|
|
240
|
+
"regex-syntax",
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
[[package]]
|
|
244
|
+
name = "regex-syntax"
|
|
245
|
+
version = "0.8.11"
|
|
246
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
247
|
+
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
248
|
+
|
|
249
|
+
[[package]]
|
|
250
|
+
name = "rustc-hash"
|
|
251
|
+
version = "2.1.3"
|
|
252
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
253
|
+
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
|
254
|
+
|
|
255
|
+
[[package]]
|
|
256
|
+
name = "seq-macro"
|
|
257
|
+
version = "0.3.6"
|
|
258
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
259
|
+
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
|
260
|
+
|
|
261
|
+
[[package]]
|
|
262
|
+
name = "shell-words"
|
|
263
|
+
version = "1.1.1"
|
|
264
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
265
|
+
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
|
266
|
+
|
|
267
|
+
[[package]]
|
|
268
|
+
name = "shlex"
|
|
269
|
+
version = "1.3.0"
|
|
270
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
271
|
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
272
|
+
|
|
273
|
+
[[package]]
|
|
274
|
+
name = "syn"
|
|
275
|
+
version = "2.0.119"
|
|
276
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
277
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
278
|
+
dependencies = [
|
|
279
|
+
"proc-macro2",
|
|
280
|
+
"quote",
|
|
281
|
+
"unicode-ident",
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
[[package]]
|
|
285
|
+
name = "unicode-ident"
|
|
286
|
+
version = "1.0.27"
|
|
287
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
288
|
+
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
|
289
|
+
|
|
290
|
+
[[package]]
|
|
291
|
+
name = "windows-link"
|
|
292
|
+
version = "0.2.1"
|
|
293
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
data/Cargo.toml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
members = ["ext/probabilistic_machines_native/core", "ext/probabilistic_machines_native/ffi"]
|
|
3
|
+
resolver = "3"
|
|
4
|
+
|
|
5
|
+
[workspace.package]
|
|
6
|
+
edition = "2024"
|
|
7
|
+
rust-version = "1.99"
|
|
8
|
+
|
|
9
|
+
[workspace.dependencies]
|
|
10
|
+
probabilistic-machines = { path = "ext/probabilistic_machines_native/core" }
|
|
11
|
+
|
|
12
|
+
[workspace.lints.rust]
|
|
13
|
+
unsafe_code = "forbid"
|
|
14
|
+
unused_must_use = "warn"
|
|
15
|
+
|
|
16
|
+
[workspace.lints.clippy]
|
|
17
|
+
all = { level = "warn", priority = -1 }
|
|
18
|
+
pedantic = { level = "warn", priority = -1 }
|
|
19
|
+
nursery = { level = "warn", priority = -1 }
|
|
20
|
+
expect_used = "deny"
|
|
21
|
+
unwrap_used = "deny"
|
|
22
|
+
uninlined_format_args = "deny"
|
|
23
|
+
needless_borrow = "deny"
|
|
24
|
+
redundant_clone = "deny"
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Abdelkader Boudih
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# ProbabilisticMachines 🔭
|
|
2
|
+
|
|
3
|
+
> **Survey the whole stream on a fixed fuel budget.** The tender that answers how
|
|
4
|
+
> many, how often and how extreme without carrying the cargo it measured.
|
|
5
|
+
|
|
6
|
+
Distinct counts, quantiles and frequencies in memory you pick up front.
|
|
7
|
+
|
|
8
|
+
Counting exactly means keeping every key: a `Set` of user ids, a `Hash` of path
|
|
9
|
+
counts, an array of latencies to sort. These five structures answer the same
|
|
10
|
+
questions from state that was sized at construction time and never grows.
|
|
11
|
+
**AMS Flajolet** takes a reading and discards the sample, which is why its hold
|
|
12
|
+
is the same size on the way home as it was at launch.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🚀 Launch Sequence
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
require 'probabilistic_machines'
|
|
20
|
+
|
|
21
|
+
visitors = ProbabilisticMachines::HyperLogLog.new(registers: 4096) # 4 KiB
|
|
22
|
+
50_000.times { |id| visitors.add("user-#{id % 12_000}") }
|
|
23
|
+
|
|
24
|
+
visitors.estimate # => 12_361.2
|
|
25
|
+
visitors.standard_error # => 0.01625
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🔩 The Native Extension Is Required
|
|
31
|
+
|
|
32
|
+
There is no pure-Ruby fallback and no `DISABLE_*` switch. HyperLogLog, Count-Min
|
|
33
|
+
Sketch, t-digest, cuckoo hashing and Space-Saving are five algorithms, and a
|
|
34
|
+
second implementation would mean maintaining each of them twice, with two sets
|
|
35
|
+
of accuracy tests and two byte formats to keep in step. The gem is a binding to
|
|
36
|
+
the `probabilistic-machines` crate in `ext/probabilistic_machines_native/core`,
|
|
37
|
+
and it needs Ruby 4.0 and a Rust toolchain (or a platform gem).
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📐 Sizes Are a Fixed Menu
|
|
42
|
+
|
|
43
|
+
The crate sizes every structure by a const generic, so each size is a separately
|
|
44
|
+
compiled type. The extension carries these and raises `ArgumentError` for
|
|
45
|
+
anything else:
|
|
46
|
+
|
|
47
|
+
| Class | Keyword | Values |
|
|
48
|
+
| ---------------- | -------------------- | ----------------------------- |
|
|
49
|
+
| `HyperLogLog` | `registers:` | 1024, 4096, 16384, 65536 |
|
|
50
|
+
| `CountMinSketch` | `width:` by `depth:` | 1024, 4096 by 4 or 8 |
|
|
51
|
+
| `CuckooFilter` | `buckets:` | 1024, 4096, 16384, 65536 |
|
|
52
|
+
| `HeavyHitters` | `k:` | 16, 64, 256 |
|
|
53
|
+
| `TDigest` | `compression:` | any, it is a runtime argument |
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
ProbabilisticMachines::HyperLogLog.new(registers: 3000)
|
|
57
|
+
# ArgumentError: registers 3000 is not supported, expected one of [1024, 4096, 16384, 65536]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🌌 How Many Distinct
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
hll = ProbabilisticMachines::HyperLogLog.new(registers: 4096)
|
|
66
|
+
hll.add('user-1')
|
|
67
|
+
hll.add_hash(0x9e37_79b9_7f4a_7c15) # for streams that ship hashes, not keys
|
|
68
|
+
hll.estimate # Float
|
|
69
|
+
hll.standard_error # 1.04 / sqrt(registers)
|
|
70
|
+
hll.merge(other) # exact union
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Memory is one byte per register, so 4096 registers cost 4 KiB whether the stream
|
|
74
|
+
held ten ids or ten billion.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 📊 How Often Each
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
cms = ProbabilisticMachines::CountMinSketch.new(width: 4096, depth: 4)
|
|
82
|
+
cms.add('/login', count: 3)
|
|
83
|
+
cms.count('/login') # never below the truth
|
|
84
|
+
cms.total # every count added
|
|
85
|
+
cms.merge(other) # exact sum
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A count is over the truth by at most `e * total / width`, with probability
|
|
89
|
+
`1 - e ** -depth` of staying inside it. Width buys accuracy, depth buys
|
|
90
|
+
confidence.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 📏 Quantiles
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
td = ProbabilisticMachines::TDigest.new(compression: 100)
|
|
98
|
+
td.add(12.5)
|
|
99
|
+
td.add(12.5, weight: 1_000.0) # one point standing for a thousand observations
|
|
100
|
+
td.quantile(0.99)
|
|
101
|
+
td.cdf(12.5)
|
|
102
|
+
td.centroids # Array of frozen Centroid(mean:, weight:)
|
|
103
|
+
td.minimum # exact, as is maximum
|
|
104
|
+
td.merge(other)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
A quantile is off by about `1 / compression` of the distribution in the middle
|
|
108
|
+
and is exact at the extremes, in `compression * ln(n)` clusters rather than `n`
|
|
109
|
+
samples. An empty digest answers `Float::NAN`.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 🛰️ Have I Seen This Contact Before
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
cf = ProbabilisticMachines::CuckooFilter.new(buckets: 4096)
|
|
117
|
+
cf.insert('device-9') # raises ProbabilisticMachines::Full when wedged
|
|
118
|
+
cf.include?('device-9') # contains? is the same question
|
|
119
|
+
cf.remove('device-9') # unlike a Bloom filter, it can forget
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`buckets * 4` items in `buckets * 4` bytes. Never a false negative; about 3%
|
|
123
|
+
false positives. Only remove items you inserted, or you take a colliding item's
|
|
124
|
+
fingerprint with it. A cuckoo filter cannot merge, so there is no `merge` here.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 👑 Who Dominates the Stream
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
hh = ProbabilisticMachines::HeavyHitters.new(k: 64)
|
|
132
|
+
hh.add('10.0.0.1')
|
|
133
|
+
hh.add('10.0.0.1', count: 5)
|
|
134
|
+
hh.top(10) # [Hitter(key:, count:, overestimate:), ...] most frequent first
|
|
135
|
+
hh.ranked # every tracked key
|
|
136
|
+
hh.observed # observations fed in
|
|
137
|
+
hh.merge(other)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
After `n` observations every key seen more than `n / k` times is tracked, in `k`
|
|
141
|
+
counters and no heap per key. A counter can inherit the count of the key it
|
|
142
|
+
evicted, or what a merged-in table's own evictions may have hidden, so the true
|
|
143
|
+
count lies between `count - overestimate` and `count`.
|
|
144
|
+
|
|
145
|
+
Keys are UTF-8 strings here: Space-Saving needs `Copy + Eq` keys in Rust, so the
|
|
146
|
+
table counts 64-bit key hashes and the extension keeps the string each tracked
|
|
147
|
+
hash came from, to hand back from `top` and `ranked`. The other four classes
|
|
148
|
+
take any byte string.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 📦 Transmitting a Reading
|
|
153
|
+
|
|
154
|
+
`dump` returns the crate's own byte frame, `load` reads one back. The frame
|
|
155
|
+
names its own shape, so `load` needs no keyword and rejects a blob written with
|
|
156
|
+
a size this build does not carry. It also reads the frame's fields against each
|
|
157
|
+
other and refuses a state no stream could have left behind, so an edited blob
|
|
158
|
+
raises instead of handing back a sketch that counts more than it ever saw.
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
blob = hll.dump # binary String
|
|
162
|
+
ProbabilisticMachines::HyperLogLog.load(blob)
|
|
163
|
+
ProbabilisticMachines::TDigest.load(blob) # ArgumentError: codec: wrong structure tag
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Anything that reads the crate reads these frames, so an embedded device can ship
|
|
167
|
+
a sketch a Rails app folds in. `HeavyHitters` is the one blob that is not purely
|
|
168
|
+
the crate's: the crate's frame counts 64-bit key hashes, so the extension appends
|
|
169
|
+
its key table after it, one record of a 64-bit byte length and that many UTF-8
|
|
170
|
+
bytes per tracked key. `load` rehashes each name, and rejects a blob whose key
|
|
171
|
+
table is malformed or does not name every key the frame tracks.
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
restored = ProbabilisticMachines::HeavyHitters.load(hh.dump)
|
|
175
|
+
restored.top(1).first.key # => '10.0.0.1', the string, not its hash
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## ⚙️ The Rust Core
|
|
181
|
+
|
|
182
|
+
The sketches themselves are the `probabilistic-machines` crate under
|
|
183
|
+
`ext/probabilistic_machines_native/core`: `no_std`, zero dependencies, allocating
|
|
184
|
+
only for `TDigest` and for the `encoded` frames this extension dumps, and
|
|
185
|
+
verified on bare metal. The binding crate beside it under
|
|
186
|
+
`ffi/` is the Ruby surface and is not published.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 📡 Credits
|
|
191
|
+
|
|
192
|
+
**AMS Flajolet**, NCC-0185, *Flajolet*-class tender.
|
|
193
|
+
|
|
194
|
+
Named for Philippe Flajolet and G. Nigel Martin's *Probabilistic Counting
|
|
195
|
+
Algorithms for Data Base Applications* (1985), and for Flajolet, Fusy, Gandouet
|
|
196
|
+
and Meunier's *HyperLogLog* (2007), which established that you can count what
|
|
197
|
+
you refuse to store.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "probabilistic-machines"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
edition.workspace = true
|
|
5
|
+
rust-version.workspace = true
|
|
6
|
+
authors = ["Abdelkader Boudih <oss@seuros.com>"]
|
|
7
|
+
license = "MIT"
|
|
8
|
+
description = "Sketches for distinct counts, quantiles and frequencies - no_std, zero dependencies"
|
|
9
|
+
repository = "https://github.com/seuros/probabilistic_machines"
|
|
10
|
+
keywords = ["hyperloglog", "t-digest", "count-min", "sketch", "no-std"]
|
|
11
|
+
categories = ["no-std", "algorithms", "data-structures"]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
|
|
14
|
+
[lib]
|
|
15
|
+
name = "probabilistic_machines"
|
|
16
|
+
crate-type = ["lib"]
|
|
17
|
+
|
|
18
|
+
[features]
|
|
19
|
+
default = ["std"]
|
|
20
|
+
std = ["alloc"]
|
|
21
|
+
alloc = []
|
|
22
|
+
|
|
23
|
+
[lints]
|
|
24
|
+
workspace = true
|