skiftet_statistical 0.4.0 → 0.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8324b9625b6fd9bc14c83e0012409543d0de68b2dd876187f76c5124afe800c6
4
- data.tar.gz: a4feb3dd72cce8563ff41c8b3e08aecdbab2c69ee8429a9e1592896b2e9f85cb
3
+ metadata.gz: d1c4747f6510fbb83eb77fe7fe5f6866f18205cf8818670de823e592683ecc4c
4
+ data.tar.gz: e7bd3286b6dbea7d1d490e9a60a18422ddf04767183cbbee17630af4261837f6
5
5
  SHA512:
6
- metadata.gz: 23fa458b6316cc5baa296ce0e33e556e0eb7b8552c939e3ac9e4dd9ffa27b24ecca44af4458403ca26c054b998a1074f5899a977ee7319c41e8c8c24a8feb00f
7
- data.tar.gz: 33cd6f0ba9b919182cdf445a41e24a3b3be45ce98356eb7c616c5f7ef6b9f9e73b211ed486e99169427d5a7083fe47b9356c289a058961c7d87cd0119e22d1fe
6
+ metadata.gz: 26d379430ba2bfcc78c0d1f25e85076be8deba58b16f987cdc9d35dc80b563c72f2452b68c991b040d693775c6e9c16405093780f593c0dd1b65c086667db033
7
+ data.tar.gz: b7718a211b88ffc453154fd8bd1dad2f797841785f37eb173139cf1da2e336c2c0de72650cd07e5cb15432a605cd4055f59378a1356cc637820bb5dc9bc97d5f
data/CHANGELOG.md CHANGED
@@ -1,6 +1,130 @@
1
1
  # Changelog
2
2
 
3
- ## [0.4.0] - unreleased
3
+ ## [0.5.0] - 2026-11-06
4
+
5
+ Three methods, a minor version by the README's Stability section: one that
6
+ lets an estimate whose arms hold a handful of visits be drawn without reading
7
+ one visit as certain, and two for intervals that always hold the figure they
8
+ stand beside. No answer moves: every 0.4.0 call answers as before, for every
9
+ input, key and seed, and every 0.4.0 spec and pin passes unedited. The
10
+ documentation and examples written since 0.4.0, which move no answer either
11
+ (the Stability and Interface contract sections, the last three items), ship in
12
+ this version too. Pure Ruby, no new dependency, Ruby >= 3.1 as before.
13
+
14
+ - `SharedCredit.with_prior(estimate, strength:)`: the estimate under a prior
15
+ pooled from its own credited arms, each arm's evidence topped up to
16
+ `strength` visits, for `LinearValue` to draw from. Why: the sandwich of an
17
+ arm of one visit, or of visits that all hold one value, is exactly 0, so
18
+ `LinearValue` drew that arm as a point, and one visit at 1 against three at
19
+ 0, 1 and 0 read P(best) 0.994; at a strength of 20 it reads 0.620 (20 000
20
+ draws at one seed). The prior's mean per part is the credited arms' mean
21
+ weighted by their Kish's sizes; its per-visit covariance is theirs (the
22
+ sandwich times Kish's size) plus their spread around that mean. An arm of
23
+ Kish's size n below the strength k takes e = k - n visits of it: means
24
+ (e * m0 + n * r) / k, covariance block
25
+ (n / k)^2 * cov + (e * V0 + (e * n / k) * g * g^T) / k^2 (g the arm's
26
+ distance from m0), every covariance with another arm scaled by n / k. An arm
27
+ at or above the strength, and an arm with no credit, keeps its means and its
28
+ covariance block bit for bit; where every credited arm is at or above it the
29
+ answer holds the given means, covariance and rounding, so every seeded draw
30
+ and summary is the same. The answer keeps the arms, parts, weight sums and
31
+ Kish's sizes (the evidence's own); its rounding is the given one's or, where
32
+ larger, that of an entry it changed. Exact: plain arithmetic, no draw.
33
+ Refused, as an `ArgumentError`: a strength below 0 or not a finite number,
34
+ an estimate without its weight sums or Kish's sizes, an arm with means but a
35
+ Kish's size of 0, and an estimate whose figures rise past a Float's largest
36
+ value under the prior (figures so small that the prior's products fall below
37
+ Float::MIN read as 0; no estimate `estimate` answers holds them).
38
+ - `BetaFunction.shortest_interval(mass, alpha, beta)`: the shortest interval
39
+ holding `mass` of Beta(alpha, beta). With one peak inside (0, 1) it is the
40
+ highest-density interval, its ends of one density, so it holds the mode; a
41
+ density that falls from 0 (alpha <= 1 <= beta) gives [0, quantile(mass)],
42
+ one that rises to 1 gives [quantile(1 - mass), 1]; a flat density the
43
+ central interval; a U-shaped one the shorter one-sided interval. Why: under
44
+ the flat prior the mode of Beta(1 + c, 1 + n - c) is the observed share
45
+ c / n, and the equal-tailed interval leaves that share out at c = 0 and
46
+ c = n (0 of 1 visit reads 0.013 to 0.842 equal-tailed, 0.000 to 0.776
47
+ shortest), and out of fractional (weighted) counts just above 0; the
48
+ shortest interval holds it for every alpha, beta >= 1. By bisection on the
49
+ side of the peak nearer 0 (a peak above 1/2 is mirrored), no draw. Refused,
50
+ as an `ArgumentError`: a mass not strictly between 0 and 1, then a
51
+ parameter whose `to_f` is not a finite number above 0.
52
+ - `BetaFunction.mean_intervals(items, mass:)`: each item's interval of a mean
53
+ of non-negative values, `{ key => { visits:, mean:, standard_error: } }` in,
54
+ `{ key => [lower, upper] }` out, read as a share of its visits: with the
55
+ visits' second moment q = n * s^2 + m^2, one event is worth u = q / m, the
56
+ item holds k = n * m^2 / q events, and the interval is u times the shortest
57
+ interval of Beta(1 + k, 1 + n - k). So the mean always lies inside, the
58
+ interval is never below 0, its variance in the normal limit is s^2 exactly,
59
+ and a value of 0 or 1 per visit gives the share's own interval. An item with
60
+ a mean of 0 (no event, a standard error of 0) takes the set's value of one
61
+ event (summed squares over summed values of the items above 0) and k = 0,
62
+ so it still has an interval, from 0, which narrows as its visits grow; with
63
+ no item above 0 it has none. An item with a mean above 0 answers from its
64
+ own figures alone, the same in any set. nil for an item without data (no
65
+ visits, no mean or no standard error). Refused, as an `ArgumentError`: a
66
+ mass not strictly between 0 and 1, items that are not a Hash of Hashes, a
67
+ figure given beside the other two that is not a finite number, a negative
68
+ visit count, mean or standard error, and an item whose arithmetic leaves a Float's range (a
69
+ value of one event that is not a finite number above 0, as only extreme
70
+ figures give it, or visits past about 2**53 that all hold one value), named
71
+ in the refusal, rather than answered with an infinite or empty interval.
72
+ - `spec/shared_credit_prior_spec.rb`: the rule by hand and against a Rational
73
+ reference that reads the rows, from rows and from per-arm sums; the answer
74
+ unchanged, and every seeded draw and summary the same, where every arm is at
75
+ or above the strength; an arm at or above it drawing the same values beside a
76
+ topped-up one in an ordinary test; a table over visit counts and goals
77
+ (values of one part, of two parts at uncertain prices, and of amounts): at a
78
+ strength of 20 a version of one visit reads P(best) between 0.41 and 0.77
79
+ (0.98 or more, or 0.02 or less, in 12 of the table's 16 rows on the estimate
80
+ itself) and none of three visits or fewer reaches 0.95 (at most 0.90); two
81
+ versions of one true value read decided (a P(best) at or above 0.95) in 0 of
82
+ 250 seeded datasets, against 158 on the estimate itself; the refusals.
83
+ - `spec/beta_function_interval_spec.rb`: the closed forms (Beta(1, b),
84
+ Beta(a, 1), the flat density, a symmetric peak), and over a table of
85
+ parameters and masses the mode held, the mass held, one density at both
86
+ ends and no wider than the equal-tailed interval or any interval of a
87
+ 400-point grid; every count of 1 to 60 visits holds its share, where the
88
+ equal-tailed interval misses exactly at 0 and at n; fractional counts just
89
+ above 0; the U-shaped and monotone cases; refusals. For the means: a 0/1
90
+ value gives the share's interval, 200 seeded sets hold every mean and never
91
+ go below 0, the zero item's closed form and its narrowing, no interval in a
92
+ set of zeros, the normal limit, an item whose visits all hold one value, an
93
+ item answered alike alone and beside others, missing data, keys and field
94
+ names, refusals, and the items whose arithmetic leaves a Float's range.
95
+ - The README's Interface contract lists the three methods, each with what it
96
+ takes, answers, guarantees (each *exact*) and refuses, as its doc comment
97
+ states them, and `spec/interface_contract_spec.rb` indexes the examples
98
+ behind their 22 guarantees.
99
+ - Costs, measured as CPU time with YJIT on one development machine while other
100
+ work kept it busy (a load average near 30): `shortest_interval` 0.06 ms for
101
+ a one-sided interval and 0.7 to 2.0 ms with one peak inside (Beta(3, 341) to
102
+ Beta(40 001, 1 160 001)); `mean_intervals` of ten items 9.9 ms;
103
+ `with_prior` of four arms and two parts 0.04 ms.
104
+ - README: a Stability section (a change that moves a seeded answer for the same
105
+ inputs is a major or minor version, as its entry here states, never a patch)
106
+ and an Interface contract section: for each method an application calls,
107
+ what it takes, what it answers, what it guarantees (determinism, which inputs
108
+ move which outputs, accuracy) and what it refuses. The same contract is in
109
+ each method's doc comment.
110
+ - `spec/interface_contract_spec.rb` indexes, method by method, the examples
111
+ that back each stated guarantee, and fails when a cited example is gone, a
112
+ listed method is no longer public, or the README's list differs.
113
+ - New examples: `BetaFunction.regularized` and `.quantile` at parameters from
114
+ 1 000 to 50 000 against an exact binomial reference, with the symmetry
115
+ identity, I_(1/2)(a, a) = 1/2, the quantile's inversion and the 2.5 % and
116
+ 97.5 % quantiles at the skew-corrected normal approximation (accuracy now
117
+ stated: 4 n ln n machine epsilons, relative, n = alpha + beta);
118
+ `Posteriors#rank`: an added item absent from `may_lead` changes neither the
119
+ others' order, the leader nor the branch over 1 000 keys with `explore` and
120
+ `min_views` set, and a `min_views` below 0 reads as none; `Prior.pooled`
121
+ refuses a negative strength with or without a group; a posterior set is a
122
+ value no reader writes to; `summary` is exact and per item;
123
+ `Draw.from_weights`' refusals; `WeightedPosterior`'s reading of its counts by
124
+ `Integer()`; `JointBeta`'s refusal of a parameter not above 0;
125
+ `sample_ratio_p_value`'s `minimum_total` above its default.
126
+
127
+ ## [0.4.0] - 2026-11-03
4
128
 
5
129
  The generic bandit: one posterior set of items, ordered and picked by one rule
6
130
  from a key, given uniforms or an RNG, with the odds of what that rule draws